diff --git a/AUTHORS b/AUTHORS new file mode 100755 index 0000000000000000000000000000000000000000..f9299b4171e0132ee58fe2a00dc56daaaa25fe0a --- /dev/null +++ b/AUTHORS @@ -0,0 +1,23 @@ + +Present authors of ntfs-3g in alphabetical order: + +Jean-Pierre Andre +Alon Bar-Lev +Martin Bene +Dominique L Bouix +Csaba Henk +Bernhard Kaindl +Erik Larsson +Alejandro Pulver +Szabolcs Szakacsits +Miklos Szeredi + + +Past authors in alphabetical order: + +Anton Altaparmakov +Mario Emmenlauer +Yuval Fledel +Yura Pakhuchiy +Richard Russon + diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..c12c4022aabce6e9e769e3c721ee35ac44c91f33 --- /dev/null +++ b/Android.mk @@ -0,0 +1,5 @@ +ifneq ($(TARGET_SIMULATOR),true) +#ifeq ($(TARGET_ARCH),x86) +include $(call all-subdir-makefiles) +#endif +endif diff --git a/COPYING b/COPYING new file mode 100755 index 0000000000000000000000000000000000000000..623b6258a134210f0b0ada106fdaab7f0370d9c5 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + 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 +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This 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 Library General +Public License instead of this License. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100755 index 0000000000000000000000000000000000000000..161a3d1d47b94f5d092b4c5fa316007c6f22fe81 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,482 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License 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 GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/CREDITS b/CREDITS new file mode 100755 index 0000000000000000000000000000000000000000..82cc12461a59fd802efab86a77108aaeee21a609 --- /dev/null +++ b/CREDITS @@ -0,0 +1,61 @@ +The following people have contributed directly or indirectly +to the ntfs-3g project. + +Please let ntfs-3g-devel@lists.sf.net know if you believe +someone is missing, or if you prefer not to be listed. + +Dominique L Bouix +Csaba Henk +Max Khon +Auri Hautamäki +Gergely Erdelyi +Anton Altaparmakov +Peter Boross +Don Bright +Mario Emmenlauer +Yuval Fledel +Kano from Kanotix +Roland Kletzing +Maarten Lankhorst +Gergely Madarasz +Patrick McLean +Florent Mertens +Yura Pakhuchiy +Miklos Szeredi +Bartosz Taudul +Zhanglinbao +Wade Fitzpatrick +Carsten Einig +Adam Cecile +Bruno Damour +Ales Fruman +Curt McDowell +Thomas Franken +Jonatan Lambert +Klaus Knopper +Zhanglinbao +Ismail Donmez +Laszlo Dvornik +Pallaghy Ajtony +Szabolcs Szakacsits +Alexei Alexandrov +Albert D. Cahalan +Russ Christensen +Pete Curran +Andras Erdei +Matthew J. Fanto +Marcin GibuÅ‚a +Christophe Grenier +Ian Jackson +Carmelo Kintana +Jan Kratochvil +Lode Leroy +David MartÃnez Moreno +Giang Nguyen +Leonard NorrgÃ¥rd +Holger Ohmacht +Per Olofsson +Yuri Per +Richard Russon +Erik Sørnes + diff --git a/ChangeLog b/ChangeLog new file mode 100755 index 0000000000000000000000000000000000000000..a49d76a1dffea8faf08004c5e3459370cedfe51f --- /dev/null +++ b/ChangeLog @@ -0,0 +1,6 @@ + +Detailed ChangeLog can be found at + http://www.tuxera.com/community/release-history/ + +The changes and history may also be found on the source repository : + http://sourceforge.net/p/ntfs-3g/ntfs-3g/ci/edge/tree/ diff --git a/INSTALL b/INSTALL new file mode 100755 index 0000000000000000000000000000000000000000..2099840756e6302d837dcd51b5dcd6262f7adb16 --- /dev/null +++ b/INSTALL @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, +Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell command `./configure && make && make install' +should configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `<wchar.h>' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf limitation. Until the limitation is lifted, you can use +this workaround: + + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. diff --git a/Makefile.am b/Makefile.am new file mode 100755 index 0000000000000000000000000000000000000000..0123e1f98081fb281acd7e8fa175609dda4fe065 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,60 @@ + +AUTOMAKE_OPTIONS = gnu +ACLOCAL_AMFLAGS = -I m4 + +EXTRA_DIST = AUTHORS CREDITS COPYING NEWS autogen.sh + +MAINTAINERCLEANFILES=\ + $(srcdir)/configure \ + $(srcdir)/Makefile.in \ + $(srcdir)/aclocal.m4 \ + $(srcdir)/compile \ + $(srcdir)/depcomp \ + $(srcdir)/install-sh \ + $(srcdir)/ltmain.sh \ + $(srcdir)/missing \ + $(srcdir)/config.guess \ + $(srcdir)/config.sub \ + $(srcdir)/config.h.in \ + $(srcdir)/config.h.in~ \ + $(srcdir)/INSTALL \ + $(srcdir)/m4/ltsugar.m4 \ + $(srcdir)/m4/libtool.m4 \ + $(srcdir)/m4/ltversion.m4 \ + $(srcdir)/m4/lt~obsolete.m4 \ + $(srcdir)/m4/ltoptions.m4 + +SUBDIRS = include libfuse-lite libntfs-3g ntfsprogs src + +doc_DATA = README + +dist-hook: + $(MKDIR_P) "$(distdir)/m4" + +libtool: $(LIBTOOL_DEPS) + $(SHELL) ./config.status --recheck + +libs: +if FUSE_INTERNAL + (cd libfuse-lite && $(MAKE) libs) || exit 1; +endif + (cd libntfs-3g && $(MAKE) libs) || exit 1; + +libntfs: + (cd libntfs-3g && $(MAKE) libs) || exit 1; + +drivers: libs + (cd src && $(MAKE) drivers) || exit 1; + +ntfsprogs: libntfs + (cd ntfsprogs && $(MAKE)) || exit 1; + +if ENABLE_NTFSPROGS +strip: + (cd ntfsprogs && $(MAKE) strip) || exit 1; + +extra: extras + +extras: libs + (cd ntfsprogs && $(MAKE) extras) || exit 1; +endif diff --git a/Makefile.in b/Makefile.in new file mode 100755 index 0000000000000000000000000000000000000000..8c03a2c5629319fec8111fa9bec19a7eea63aaf7 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,941 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = . +DIST_COMMON = INSTALL NEWS README AUTHORS ChangeLog \ + $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/configure $(am__configure_deps) \ + $(srcdir)/config.h.in COPYING COPYING.LIB compile config.guess \ + config.sub install-sh missing ltmain.sh +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(docdir)" +DATA = $(doc_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + cscope distdir dist dist-all distcheck +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ + $(LISP)config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FUSE_MODULE_CFLAGS = @FUSE_MODULE_CFLAGS@ +FUSE_MODULE_LIBS = @FUSE_MODULE_LIBS@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDCONFIG = @LDCONFIG@ +LDFLAGS = @LDFLAGS@ +LIBFUSE_LITE_CFLAGS = @LIBFUSE_LITE_CFLAGS@ +LIBFUSE_LITE_LIBS = @LIBFUSE_LITE_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBNTFS_3G_VERSION = @LIBNTFS_3G_VERSION@ +LIBNTFS_CPPFLAGS = @LIBNTFS_CPPFLAGS@ +LIBNTFS_LIBS = @LIBNTFS_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MKNTFS_CPPFLAGS = @MKNTFS_CPPFLAGS@ +MKNTFS_LIBS = @MKNTFS_LIBS@ +MV = @MV@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NTFSPROGS_STATIC_LIBS = @NTFSPROGS_STATIC_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OUTPUT_FORMAT = @OUTPUT_FORMAT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +RM = @RM@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +all_includes = @all_includes@ +all_libraries = @all_libraries@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +ntfs3gincludedir = @ntfs3gincludedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rootbindir = @rootbindir@ +rootlibdir = @rootlibdir@ +rootsbindir = @rootsbindir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = gnu +ACLOCAL_AMFLAGS = -I m4 +EXTRA_DIST = AUTHORS CREDITS COPYING NEWS autogen.sh +MAINTAINERCLEANFILES = \ + $(srcdir)/configure \ + $(srcdir)/Makefile.in \ + $(srcdir)/aclocal.m4 \ + $(srcdir)/compile \ + $(srcdir)/depcomp \ + $(srcdir)/install-sh \ + $(srcdir)/ltmain.sh \ + $(srcdir)/missing \ + $(srcdir)/config.guess \ + $(srcdir)/config.sub \ + $(srcdir)/config.h.in \ + $(srcdir)/config.h.in~ \ + $(srcdir)/INSTALL \ + $(srcdir)/m4/ltsugar.m4 \ + $(srcdir)/m4/libtool.m4 \ + $(srcdir)/m4/ltversion.m4 \ + $(srcdir)/m4/lt~obsolete.m4 \ + $(srcdir)/m4/ltoptions.m4 + +SUBDIRS = include libfuse-lite libntfs-3g ntfsprogs src +doc_DATA = README +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool config.lt +install-docDATA: $(doc_DATA) + @$(NORMAL_INSTALL) + @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ + done + +uninstall-docDATA: + @$(NORMAL_UNINSTALL) + @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build \ + && ../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile $(DATA) config.h +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(docdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr \ + distclean-libtool distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-docDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-docDATA + +.MAKE: $(am__recursive_targets) all install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--refresh check check-am clean clean-cscope clean-generic \ + clean-libtool cscope cscopelist-am ctags ctags-am dist \ + dist-all dist-bzip2 dist-gzip dist-hook dist-lzip dist-shar \ + dist-tarZ dist-xz dist-zip distcheck distclean \ + distclean-generic distclean-hdr distclean-libtool \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-docDATA install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am uninstall-docDATA + + +dist-hook: + $(MKDIR_P) "$(distdir)/m4" + +libtool: $(LIBTOOL_DEPS) + $(SHELL) ./config.status --recheck + +libs: +@FUSE_INTERNAL_TRUE@ (cd libfuse-lite && $(MAKE) libs) || exit 1; + (cd libntfs-3g && $(MAKE) libs) || exit 1; + +libntfs: + (cd libntfs-3g && $(MAKE) libs) || exit 1; + +drivers: libs + (cd src && $(MAKE) drivers) || exit 1; + +ntfsprogs: libntfs + (cd ntfsprogs && $(MAKE)) || exit 1; + +@ENABLE_NTFSPROGS_TRUE@strip: +@ENABLE_NTFSPROGS_TRUE@ (cd ntfsprogs && $(MAKE) strip) || exit 1; + +@ENABLE_NTFSPROGS_TRUE@extra: extras + +@ENABLE_NTFSPROGS_TRUE@extras: libs +@ENABLE_NTFSPROGS_TRUE@ (cd ntfsprogs && $(MAKE) extras) || exit 1; + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/NEWS b/NEWS new file mode 100755 index 0000000000000000000000000000000000000000..3a7effdeb62795189b1789e8635b71b95f0de695 --- /dev/null +++ b/NEWS @@ -0,0 +1,5 @@ + +Project news are at http://tuxera.com/community/ntfs-3g-download/ + +Release notes are maintained at http://tuxera.com/community/release-history/ + diff --git a/README b/README new file mode 100755 index 0000000000000000000000000000000000000000..0f6a7db2d0e06817ff224c26f7cca68f28010e72 --- /dev/null +++ b/README @@ -0,0 +1,153 @@ + +INTRODUCTION +============ + +The NTFS-3G driver is an open source, freely available read/write NTFS driver +for Linux, FreeBSD, Mac OS X, NetBSD, OpenSolaris, QNX and Haiku. It provides +safe and fast handling of the Windows XP, Windows Server 2003, Windows 2000, +Windows Vista, Windows Server 2008 and Windows 7 file systems. + +The purpose of the project is to develop, quality assurance and support a +trustable, featureful and high performance solution for hardware platforms +and operating systems whose users need to reliably interoperate with NTFS. +Besides this practical goal, the project also aims to explore the limits +of the hybrid, kernel/user space filesystem driver approach, performance, +reliability and feature richness per invested effort wise. + +Besides the common file system features, NTFS-3G has support for file +ownership and permissions, POSIX ACLs, junction points, extended attributes +and creating compressed files. Parameter files in the directory .NTFS-3G may +be required to enable them, please get the instructions from + + http://www.tuxera.com/community/ntfs-3g-advanced/ + +News, support answers, problem submission instructions, support and discussion +forums, performance numbers and other information are available on the project +web site at + + http://www.tuxera.com/community/ + +LICENSES +======== + +All the NTFS related components : the file system drivers, the ntfsprogs +utilities and the shared library libntfs-3g are distributed 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. See the included file COPYING. + +The fuse-lite library is distributed under the terms of the GNU LGPLv2. +See the included file COPYING.LIB. + +QUICK INSTALLATION +================== + +Linux: Make sure you have the basic development tools and the kernel includes +the FUSE kernel module. Then unpack the source tarball and type: + + ./configure + make + make install # or 'sudo make install' if you aren't root. + +Please note that NTFS-3G doesn't require the FUSE user space package any +more. + +The list of options for building specific configurations is displayed by +typing : + + ./configure --help + +Below are a few specific options to ./configure : + --disable-ntfsprogs : do not build the ntfsprogs tools, + --enable-posix-acls : enable support for Posix ACLs + --enable-xattr-mappings : enable system extended attributes mappings + --with-fuse=external : use external fuse (overriding Linux default) + +There are also a few make targets for building parts : + make libntfs : only build the libntfs-3g library + make libs : only build libntfs-3g (and libfuse-lite, if relevant) + make drivers : only build drivers and libraries, without ntfsprogs + make ntfsprogs : only build ntfsprogs and libntfs-3g, without drivers + +Non-Linux: Please see + + http://www.tuxera.com/community/ntfs-3g-download/ + +for known OS specific installation and source packages, but generally +the same procedures apply. + +USAGE +===== + +If there was no error during installation then the NTFS volume can be +read-write mounted for everybody the following way as the root user +(unmount the volume if it was already mounted, and replace /dev/sda1 +and /mnt/windows, if needed): + + mount -t ntfs-3g /dev/sda1 /mnt/windows +or + ntfs-3g /dev/sda1 /mnt/windows + +Please see the ntfs-3g manual page for more options and examples. + +You can also make NTFS to be mounted during boot by putting the below +line at the END(!) of the /etc/fstab file: + + /dev/sda1 /mnt/windows ntfs-3g defaults 0 0 + + +TESTING WITHOUT INSTALLING +========================= + +Newer versions of ntfs-3g can be tested without installing anything and +without disturbing an existing installation. Just configure and make as +shown previously. This will create the scripts ntfs-3g and lowntfs-3g +in the src directory, which you may activate for testing : + + ./configure + make + +then, as root : + src/ntfs-3g [-o mount-options] /dev/sda1 /mnt/windows + +And, to end the test, unmount the usual way : + umount /dev/sda1 + + +NTFS UTILITIES +============== + +The ntfsprogs includes utilities for doing all required tasks to NTFS +partitions. In general, just run a utility without any command line +options to display the version number and usage syntax. + +The following utilities are so far implemented: + +ntfsfix - Attempt to fix an NTFS partition and force Windows to check NTFS. + +mkntfs - Format a partition with the NTFS filesystem. See man 8 mkntfs for +command line options. + +ntfslabel - Display/change the label of an NTFS partition. See man 8 ntfslabel +for details. + +ntfsundelete - Recover deleted files from an NTFS volume. See man 8 +ntfsundelete for more details. + +ntfsresize - Resize NTFS volumes. See man 8 ntfsresize for details. + +ntfsclone - Efficiently create/restore an image of an NTFS partition. See +man 8 ntfsclone for details. + +ntfscluster - Locate the owner of any given sector or cluster on an NTFS +partition. See man 8 ntfscluster for details. + +ntfsinfo - Show some information about an NTFS partition or one of the files +or directories within it. See man 8 ntfsinfo for details. + +ntfsls - List information about files in a directory residing on an NTFS +partition. See man 8 ntfsls for details. + +ntfscat - Concatenate files and print their contents on the standard output. + +ntfscp - Overwrite files on an NTFS partition. diff --git a/TODO.ntfsprogs b/TODO.ntfsprogs new file mode 100755 index 0000000000000000000000000000000000000000..3a60421f459263a9929297f9c83015777bb84645 --- /dev/null +++ b/TODO.ntfsprogs @@ -0,0 +1,126 @@ +Please keep in alphabetical order so utilities are easier to find. + +Thanks, + Anton + + +********** +* mkntfs * +********** + +- Correct support for creating volumes with larger sector sizes (mft record + size, cluster size, and index block size must be >= sector size), so for 1k, + 2k, and 4k sectors, we need to set the default mft record, cluster, and index + block size to be at least the sector size. +- Correct the odd last partition sector not being accessible under 2.4 kernels + by setting the device block size to the sector size (default is 1k on 2.4 + kernels and they can't cope with partial sectors). +- Got a report that creating a floppy with mkntfs failed. Difference between + this floppy and the floppy created by the special tool found on the net was + said to be that the bitmap is 256kib on the special floppy while mkntfs will + make it much smaller. Need to verify this and experiment with the bitmap + size to make it work. Note, reporter was using win2k. + + +************* +* ntfsclone * +************* + +- get rid of the unneeded lseek()'s during reads/writes (probably it + doesn't improve performance much, or any at all) +- catch if source and dest are the same +- disable consistency check for --metadata (e.g. if the check is crashing) +- option: --inode +- option: --data +- metadata cloning: skip more non-needed inodes +- manual: document LFS issues (smbfs' lfs option, nfs) +- manual: mention optimized seeks +- manual: optimal backup if disks have bad sectors +- manual: ntfsclone guarantees the restored image works only + if one restores to the exactly same partition. For example, + one can not copy system partition to a different partition: + minimum "hidden sectors" field and BOOT.INI need modifications. + We could do these adjustments optionally. +- check if kernel block size = GCD(page size, device size) makes + effect on performance (Al Viro says no) +- check whether the O_WRONLY -> O_RDWR change made effect on performance + + +*********** +* ntfscmp * +*********** + +- compare mft record headers +- exit status is 0 if inputs are the same, 1 if different, other if trouble +- optionally ignore less interesting fields (e.g. attribute instance) +- new option: --metadata mode +- unnamed resident attributes with same type are ignored +- code cleanup, remove many cross-util duplicates +- handle deleted records +- performance: special handling for sparse files + + +********** +* ntfscp * +********** + +- add ability to copy multiple files at once. + + +*********** +* ntfsfix * +*********** + +- Cleanup to use ntfs_attr_* API for editing $MFTMirr, $Volume, and $LogFile. + This has the immediate benefit of enabling attribute list support and making + the code simpler. +- On ntfs 3.0+ volumes need to disable the usn journal if it is active. This + means deleting file $UsnJrnl from /$Extend directory. +- On ntfs 3.0+ volumes need to mark the quota out of date? - Probably, but + it shouldn't cause any corruption not doing so for the moment so this is + not a showstopper bug for the first release. (AIA) + + +************* +* ntfslabel * +************* + +- Support ioctls for kernel driver and ntfsmount for reading/changing the label. + + +************* +* ntfsmount * +************* + + + +************** +* ntfsresize * +************** + +High priority + - move ntfs consistency check to libntfs (for ntfsck, ntfsclone, etc) + - use different exit codes (e.g. corrupt volume detected, unsupported case, + bad sectors, etc) + +Medium priority + - cope with the rare, unsupported cases, see man ntfsresize 'KNOWN ISSUES' + - save $Bitmap if it was modified and an error occures (e.g. bad sector). + - handle signals (^C, etc) + +Low priority + - fully support disks with bad sectors (attrlist attr, unknown bad sectors) + - move volume start + + +**************** +* ntfsundelete * +**************** + +- undelete by name rather than inode number +- support for compressed files +- support for internationalisation +- recover by type? +- mass undelete (using wildcards) +- display parent directory +- name "<none>" to MFTn diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100755 index 0000000000000000000000000000000000000000..3ae1bb4489dfee939af3e7d52d067228f480fcb0 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,1528 @@ +# generated automatically by aclocal 1.14.1 -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +dnl Autoconf macros for libgcrypt +dnl Copyright (C) 2002, 2004, 2011 Free Software Foundation, Inc. +dnl +dnl This file is free software; as a special exception the author gives +dnl unlimited permission to copy and/or distribute it, with or without +dnl modifications, as long as this notice is preserved. +dnl +dnl This file is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +dnl implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + +dnl AM_PATH_LIBGCRYPT([MINIMUM-VERSION, +dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) +dnl Test for libgcrypt and define LIBGCRYPT_CFLAGS and LIBGCRYPT_LIBS. +dnl MINIMUN-VERSION is a string with the version number optionalliy prefixed +dnl with the API version to also check the API compatibility. Example: +dnl a MINIMUN-VERSION of 1:1.2.5 won't pass the test unless the installed +dnl version of libgcrypt is at least 1.2.5 *and* the API number is 1. Using +dnl this features allows to prevent build against newer versions of libgcrypt +dnl with a changed API. +dnl +AC_DEFUN([AM_PATH_LIBGCRYPT], +[ AC_REQUIRE([AC_CANONICAL_HOST]) + AC_ARG_WITH(libgcrypt-prefix, + AC_HELP_STRING([--with-libgcrypt-prefix=PFX], + [prefix where LIBGCRYPT is installed (optional)]), + libgcrypt_config_prefix="$withval", libgcrypt_config_prefix="") + if test x$libgcrypt_config_prefix != x ; then + if test x${LIBGCRYPT_CONFIG+set} != xset ; then + LIBGCRYPT_CONFIG=$libgcrypt_config_prefix/bin/libgcrypt-config + fi + fi + + AC_PATH_TOOL(LIBGCRYPT_CONFIG, libgcrypt-config, no) + tmp=ifelse([$1], ,1:1.2.0,$1) + if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then + req_libgcrypt_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` + min_libgcrypt_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` + else + req_libgcrypt_api=0 + min_libgcrypt_version="$tmp" + fi + + AC_MSG_CHECKING(for LIBGCRYPT - version >= $min_libgcrypt_version) + ok=no + if test "$LIBGCRYPT_CONFIG" != "no" ; then + req_major=`echo $min_libgcrypt_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` + req_minor=`echo $min_libgcrypt_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` + req_micro=`echo $min_libgcrypt_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` + libgcrypt_config_version=`$LIBGCRYPT_CONFIG --version` + major=`echo $libgcrypt_config_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` + minor=`echo $libgcrypt_config_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` + micro=`echo $libgcrypt_config_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'` + if test "$major" -gt "$req_major"; then + ok=yes + else + if test "$major" -eq "$req_major"; then + if test "$minor" -gt "$req_minor"; then + ok=yes + else + if test "$minor" -eq "$req_minor"; then + if test "$micro" -ge "$req_micro"; then + ok=yes + fi + fi + fi + fi + fi + fi + if test $ok = yes; then + AC_MSG_RESULT([yes ($libgcrypt_config_version)]) + else + AC_MSG_RESULT(no) + fi + if test $ok = yes; then + # If we have a recent libgcrypt, we should also check that the + # API is compatible + if test "$req_libgcrypt_api" -gt 0 ; then + tmp=`$LIBGCRYPT_CONFIG --api-version 2>/dev/null || echo 0` + if test "$tmp" -gt 0 ; then + AC_MSG_CHECKING([LIBGCRYPT API version]) + if test "$req_libgcrypt_api" -eq "$tmp" ; then + AC_MSG_RESULT([okay]) + else + ok=no + AC_MSG_RESULT([does not match. want=$req_libgcrypt_api got=$tmp]) + fi + fi + fi + fi + if test $ok = yes; then + LIBGCRYPT_CFLAGS=`$LIBGCRYPT_CONFIG --cflags` + LIBGCRYPT_LIBS=`$LIBGCRYPT_CONFIG --libs` + ifelse([$2], , :, [$2]) + libgcrypt_config_host=`$LIBGCRYPT_CONFIG --host 2>/dev/null || echo none` + if test x"$libgcrypt_config_host" != xnone ; then + if test x"$libgcrypt_config_host" != x"$host" ; then + AC_MSG_WARN([[ +*** +*** The config script $LIBGCRYPT_CONFIG was +*** built for $libgcrypt_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-libgcrypt-prefix +*** to specify a matching config script. +***]]) + fi + fi + else + LIBGCRYPT_CFLAGS="" + LIBGCRYPT_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(LIBGCRYPT_CFLAGS) + AC_SUBST(LIBGCRYPT_LIBS) +]) + +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 1 (pkg-config-0.24) +# +# Copyright © 2004 Scott James Remnant <scott@netsplit.com>. +# +# 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. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +# only at the first occurence in configure.ac, so if the first place +# it's called might be skipped (such as if it is within an "if", you +# have to call PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])# PKG_CHECK_MODULES + + +# PKG_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable pkgconfigdir as the location where a module +# should install pkg-config .pc files. By default the directory is +# $libdir/pkgconfig, but the default can be changed by passing +# DIRECTORY. The user can override through the --with-pkgconfigdir +# parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_INSTALLDIR + + +# PKG_NOARCH_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable noarch_pkgconfigdir as the location where a +# module should install arch-independent pkg-config .pc files. By +# default the directory is $datadir/pkgconfig, but the default can be +# changed by passing DIRECTORY. The user can override through the +# --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_NOARCH_INSTALLDIR + + +# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# ------------------------------------------- +# Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])# PKG_CHECK_VAR + +# Copyright (C) 2002-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.14' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.14.1], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.14.1])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[dnl Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50])dnl +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each '.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html> +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542> + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: <http://www.gnu.org/software/coreutils/>. + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- +# From Jim Meyering + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAINTAINER_MODE([DEFAULT-MODE]) +# ---------------------------------- +# Control maintainer-specific portions of Makefiles. +# Default is to disable them, unless 'enable' is passed literally. +# For symmetry, 'disable' may be passed as well. Anyway, the user +# can override the default with the --enable/--disable switch. +AC_DEFUN([AM_MAINTAINER_MODE], +[m4_case(m4_default([$1], [disable]), + [enable], [m4_define([am_maintainer_other], [disable])], + [disable], [m4_define([am_maintainer_other], [enable])], + [m4_define([am_maintainer_other], [enable]) + m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) +AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode's default is 'disable' unless 'enable' is passed + AC_ARG_ENABLE([maintainer-mode], + [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], + am_maintainer_other[ make rules and dependencies not useful + (and sometimes confusing) to the casual installer])], + [USE_MAINTAINER_MODE=$enableval], + [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST([MAINT])dnl +] +) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar <conftest.tar]) + AM_RUN_LOG([cat conftest.dir/file]) + grep GrepMe conftest.dir/file >/dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([m4/libtool.m4]) +m4_include([m4/ltoptions.m4]) +m4_include([m4/ltsugar.m4]) +m4_include([m4/ltversion.m4]) +m4_include([m4/lt~obsolete.m4]) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000000000000000000000000000000000000..62e5439b6540ae0d9a6891cdd23ad86ed32bbd56 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# Run this to generate configure, Makefile.in's, etc + +(autoreconf --version) < /dev/null > /dev/null 2>&1 || { + (autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have the GNU Build System (autoconf, automake, " + echo "libtool, etc) to update the ntfs-3g build system. Download the " + echo "appropriate packages for your distribution, or get the source " + echo "tar balls from ftp://ftp.gnu.org/pub/gnu/." + exit 1 + } + echo + echo "**Error**: Your version of autoconf is too old (you need at least 2.57)" + echo "to update the ntfs-3g build system. Download the appropriate " + echo "updated package for your distribution, or get the source tar ball " + echo "from ftp://ftp.gnu.org/pub/gnu/." + exit 1 +} + +echo Running autoreconf --verbose --install --force +autoreconf --verbose --install --force diff --git a/compile b/compile new file mode 100755 index 0000000000000000000000000000000000000000..531136b068ef00e23d38429e6ee9a57d581a0870 --- /dev/null +++ b/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Written by Tom Tromey <tromey@cygnus.com>. +# +# 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, 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 <http://www.gnu.org/licenses/>. + +# 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. + +# This file is maintained in Automake, please report +# bugs to <bug-automake@gnu.org> or send patches to +# <automake-patches@gnu.org>. + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to <bug-automake@gnu.org>. +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/config.guess b/config.guess new file mode 100755 index 0000000000000000000000000000000000000000..1f5c50c0d1529d50b94dc3533ca72a47f0fa5849 --- /dev/null +++ b/config.guess @@ -0,0 +1,1420 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2014 Free Software Foundation, Inc. + +timestamp='2014-03-23' + +# This file 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 <http://www.gnu.org/licenses/>. +# +# 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. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# +# Please send patches with a ChangeLog entry to config-patches@gnu.org. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2014 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include <features.h> + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include <stdio.h> /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <sys/systemcfg.h> + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include <stdlib.h> + #include <unistd.h> + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <unistd.h> + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + *:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + else + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` + echo ${UNAME_MACHINE}-pc-isc$UNAME_REL + elif /bin/uname -X 2>/dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says <Richard.M.Bartel@ccMail.Census.GOV> + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes <hewes@openmarket.com>. + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; +esac + +cat >&2 <<EOF +$0: unable to guess system type + +This script, last modified $timestamp, has failed to recognize +the operating system you are using. It is advised that you +download the most up to date version of the config scripts from + + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +and + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +If the version you run ($0) is already up to date, please +send the following data and any information you think might be +pertinent to <config-patches@gnu.org> in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config.h b/config.h new file mode 100755 index 0000000000000000000000000000000000000000..c232cf84a0bb73043903749c1308aa3d9dad2140 --- /dev/null +++ b/config.h @@ -0,0 +1,447 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define this to 1 if you want to enable support of encrypted files in + libntfs and utilities. */ +/* #undef ENABLE_CRYPTO */ + +/* Define to 1 if debug should be enabled */ +/* #undef ENABLE_DEBUG */ + +/* Define this to 1 if you want to enable use of Windows compliant disk + geometry. */ +/* #undef ENABLE_HD */ + +/* Define to 1 if the nfconv patch should be enabled */ +/* #undef ENABLE_NFCONV */ + +/* Define this to 1 if you want to enable generation of DCE compliant UUIDs. + */ +/* #undef ENABLE_UUID */ + +/* Define to 1 if using internal fuse */ +#define FUSE_INTERNAL 1 + +/* Define to 1 if you have the `atexit' function. */ +#define HAVE_ATEXIT 1 + +/* Define to 1 if you have the `basename' function. */ +#define HAVE_BASENAME 1 + +/* Define to 1 if you have the <byteswap.h> header file. */ +#define HAVE_BYTESWAP_H 1 + +/* Define to 1 if you have the `clock_gettime' function. */ +#define HAVE_CLOCK_GETTIME 1 + +/* Define to 1 if you have the <ctype.h> header file. */ +#define HAVE_CTYPE_H 1 + +/* Define to 1 if you have the `daemon' function. */ +#define HAVE_DAEMON 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the `dup2' function. */ +#define HAVE_DUP2 1 + +/* Define to 1 if you have the <endian.h> header file. */ +// #define HAVE_ENDIAN_H 1 + +/* Define to 1 if you have the <errno.h> header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `fdatasync' function. */ +#define HAVE_FDATASYNC 1 + +/* Define to 1 if you have the <features.h> header file. */ +#define HAVE_FEATURES_H 1 + +/* Define to 1 if you have the `ffs' function. */ +#define HAVE_FFS 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if you have the `getmntent' function. */ +#define HAVE_GETMNTENT 1 + +/* Define to 1 if you have the <getopt.h> header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getopt_long' function. */ +#define HAVE_GETOPT_LONG 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `hasmntopt' function. */ +// #define HAVE_HASMNTOPT 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <libgen.h> header file. */ +#define HAVE_LIBGEN_H 1 + +/* Define to 1 if you have the <libintl.h> header file. */ +// #define HAVE_LIBINTL_H 1 + +/* Define to 1 if you have the <limits.h> header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the <linux/fd.h> header file. */ +#define HAVE_LINUX_FD_H 1 + +/* Define to 1 if you have the <linux/fs.h> header file. */ +#define HAVE_LINUX_FS_H 1 + +/* Define to 1 if you have the <linux/hdreg.h> header file. */ +#define HAVE_LINUX_HDREG_H 1 + +/* Define to 1 if you have the <linux/major.h> header file. */ +#define HAVE_LINUX_MAJOR_H 1 + +/* Define to 1 if you have the <locale.h> header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the <machine/endian.h> header file. */ +/* #undef HAVE_MACHINE_ENDIAN_H */ + +/* Define to 1 if you have the <malloc.h> header file. */ +// #define HAVE_MALLOC_H 1 + +/* Define to 1 if mbrtowc and mbstate_t are properly declared. */ +#define HAVE_MBRTOWC 1 + +/* Define to 1 if you have the `mbsinit' function. */ +#define HAVE_MBSINIT 1 + +/* Define to 1 if you have the `memcpy' function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the <mntent.h> header file. */ +#define HAVE_MNTENT_H 1 + +/* Define to 1 if you have the <pwd.h> header file. */ +#define HAVE_PWD_H 1 + +/* Define to 1 if you have the `random' function. */ +#define HAVE_RANDOM 1 + +/* Define to 1 if you have the `realpath' function. */ +#define HAVE_REALPATH 1 + +/* Define to 1 if you have the `regcomp' function. */ +#define HAVE_REGCOMP 1 + +/* Define to 1 if you have the <regex.h> header file. */ +#define HAVE_REGEX_H 1 + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `setxattr' function. */ +#define HAVE_SETXATTR 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +/* #undef HAVE_STAT_EMPTY_STRING_BUG */ + +/* Define to 1 if you have the <stdarg.h> header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if stdbool.h conforms to C99. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the <stddef.h> header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdio.h> header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strnlen' function. */ +#define HAVE_STRNLEN 1 + +/* Define to 1 if you have the `strsep' function. */ +#define HAVE_STRSEP 1 + +/* Define to 1 if you have the `strtol' function. */ +#define HAVE_STRTOL 1 + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if `st_atim' is a member of `struct stat'. */ +// #define HAVE_STRUCT_STAT_ST_ATIM 1 + +/* Define to 1 if `st_atimensec' is a member of `struct stat'. */ +/* #undef HAVE_STRUCT_STAT_ST_ATIMENSEC */ + +/* Define to 1 if `st_atimespec' is a member of `struct stat'. */ +/* #undef HAVE_STRUCT_STAT_ST_ATIMESPEC */ + +/* Define to 1 if `st_blocks' is a member of `struct stat'. */ +#define HAVE_STRUCT_STAT_ST_BLOCKS 1 + +/* Define to 1 if `st_rdev' is a member of `struct stat'. */ +#define HAVE_STRUCT_STAT_ST_RDEV 1 + +/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use + `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ +#define HAVE_ST_BLOCKS 1 + +/* Define to 1 if you have the `sysconf' function. */ +#define HAVE_SYSCONF 1 + +/* Define to 1 if you have the <syslog.h> header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the <sys/byteorder.h> header file. */ +/* #undef HAVE_SYS_BYTEORDER_H */ + +/* Define to 1 if you have the <sys/disk.h> header file. */ +/* #undef HAVE_SYS_DISK_H */ + +/* Define to 1 if you have the <sys/endian.h> header file. */ +/* #undef HAVE_SYS_ENDIAN_H */ + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the <sys/mkdev.h> header file. */ +/* #undef HAVE_SYS_MKDEV_H */ + +/* Define to 1 if you have the <sys/mount.h> header file. */ +#define HAVE_SYS_MOUNT_H 1 + +/* Define to 1 if you have the <sys/param.h> header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the <sys/statvfs.h> header file. */ +#define HAVE_SYS_STATVFS_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/sysmacros.h> header file. */ +#define HAVE_SYS_SYSMACROS_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <sys/vfs.h> header file. */ +#define HAVE_SYS_VFS_H 1 + +/* Define to 1 if you have the <time.h> header file. */ +#define HAVE_TIME_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `utime' function. */ +#define HAVE_UTIME 1 + +/* Define to 1 if you have the `utimensat' function. */ +// #define HAVE_UTIMENSAT 1 + +/* Define to 1 if you have the <utime.h> header file. */ +#define HAVE_UTIME_H 1 + +/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */ +#define HAVE_UTIME_NULL 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the <wchar.h> header file. */ +#define HAVE_WCHAR_H 1 + +/* Define to 1 if you have the <windows.h> header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to 1 if the system has the type `_Bool'. */ +#define HAVE__BOOL 1 + +/* Don't update /etc/mtab */ +/* #undef IGNORE_MTAB */ + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Don't use default IO ops */ +/* #undef NO_NTFS_DEVICE_DEFAULT_IO_OPS */ + +/* Name of package */ +#define PACKAGE "ntfs-3g" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "ntfs-3g" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "ntfs-3g 2015.3.14" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "ntfs-3g" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2015.3.14" + +/* POSIX ACL support */ +/* #undef POSIXACLS */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Version number of package */ +#define VERSION "2015.3.14" + +/* Define to 1 if this is a Windows OS */ +/* #undef WINDOWS */ + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to 1 if your processor stores words with the least significant byte + first (like Intel and VAX, unlike Motorola and SPARC). */ +#define WORDS_LITTLEENDIAN 1 + +/* system extended attributes mappings */ +/* #undef XATTR_MAPPINGS */ + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Required define if using POSIX threads */ +#define _REENTRANT 1 + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to `long int' if <sys/types.h> does not define. */ +/* #undef off_t */ + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +/* #undef size_t */ + +#define S_IREAD S_IRUSR +#define S_IWRITE S_IWUSR +#define S_IEXEC S_IXUSR + +#include <sys/types.h> +#define off_t long long diff --git a/config.h.in b/config.h.in new file mode 100755 index 0000000000000000000000000000000000000000..80027db44659fb0c3bb8c4229960eef3b703cd41 --- /dev/null +++ b/config.h.in @@ -0,0 +1,439 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Define this to 1 if you want to enable support of encrypted files in + libntfs and utilities. */ +#undef ENABLE_CRYPTO + +/* Define to 1 if debug should be enabled */ +#undef ENABLE_DEBUG + +/* Define this to 1 if you want to enable use of Windows compliant disk + geometry. */ +#undef ENABLE_HD + +/* Define to 1 if the nfconv patch should be enabled */ +#undef ENABLE_NFCONV + +/* Define this to 1 if you want to enable generation of DCE compliant UUIDs. + */ +#undef ENABLE_UUID + +/* Define to 1 if using internal fuse */ +#undef FUSE_INTERNAL + +/* Define to 1 if you have the `atexit' function. */ +#undef HAVE_ATEXIT + +/* Define to 1 if you have the `basename' function. */ +#undef HAVE_BASENAME + +/* Define to 1 if you have the <byteswap.h> header file. */ +#undef HAVE_BYTESWAP_H + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + +/* Define to 1 if you have the <ctype.h> header file. */ +#undef HAVE_CTYPE_H + +/* Define to 1 if you have the `daemon' function. */ +#undef HAVE_DAEMON + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the `dup2' function. */ +#undef HAVE_DUP2 + +/* Define to 1 if you have the <endian.h> header file. */ +#undef HAVE_ENDIAN_H + +/* Define to 1 if you have the <errno.h> header file. */ +#undef HAVE_ERRNO_H + +/* Define to 1 if you have the <fcntl.h> header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fdatasync' function. */ +#undef HAVE_FDATASYNC + +/* Define to 1 if you have the <features.h> header file. */ +#undef HAVE_FEATURES_H + +/* Define to 1 if you have the `ffs' function. */ +#undef HAVE_FFS + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `getmntent' function. */ +#undef HAVE_GETMNTENT + +/* Define to 1 if you have the <getopt.h> header file. */ +#undef HAVE_GETOPT_H + +/* Define to 1 if you have the `getopt_long' function. */ +#undef HAVE_GETOPT_LONG + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the `hasmntopt' function. */ +#undef HAVE_HASMNTOPT + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the <libgen.h> header file. */ +#undef HAVE_LIBGEN_H + +/* Define to 1 if you have the <libintl.h> header file. */ +#undef HAVE_LIBINTL_H + +/* Define to 1 if you have the <limits.h> header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the <linux/fd.h> header file. */ +#undef HAVE_LINUX_FD_H + +/* Define to 1 if you have the <linux/fs.h> header file. */ +#undef HAVE_LINUX_FS_H + +/* Define to 1 if you have the <linux/hdreg.h> header file. */ +#undef HAVE_LINUX_HDREG_H + +/* Define to 1 if you have the <linux/major.h> header file. */ +#undef HAVE_LINUX_MAJOR_H + +/* Define to 1 if you have the <locale.h> header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1 if you have the <machine/endian.h> header file. */ +#undef HAVE_MACHINE_ENDIAN_H + +/* Define to 1 if you have the <malloc.h> header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if mbrtowc and mbstate_t are properly declared. */ +#undef HAVE_MBRTOWC + +/* Define to 1 if you have the `mbsinit' function. */ +#undef HAVE_MBSINIT + +/* Define to 1 if you have the `memcpy' function. */ +#undef HAVE_MEMCPY + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define to 1 if you have the <mntent.h> header file. */ +#undef HAVE_MNTENT_H + +/* Define to 1 if you have the <pwd.h> header file. */ +#undef HAVE_PWD_H + +/* Define to 1 if you have the `random' function. */ +#undef HAVE_RANDOM + +/* Define to 1 if you have the `realpath' function. */ +#undef HAVE_REALPATH + +/* Define to 1 if you have the `regcomp' function. */ +#undef HAVE_REGCOMP + +/* Define to 1 if you have the <regex.h> header file. */ +#undef HAVE_REGEX_H + +/* Define to 1 if you have the `setlocale' function. */ +#undef HAVE_SETLOCALE + +/* Define to 1 if you have the `setxattr' function. */ +#undef HAVE_SETXATTR + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +#undef HAVE_STAT_EMPTY_STRING_BUG + +/* Define to 1 if you have the <stdarg.h> header file. */ +#undef HAVE_STDARG_H + +/* Define to 1 if stdbool.h conforms to C99. */ +#undef HAVE_STDBOOL_H + +/* Define to 1 if you have the <stddef.h> header file. */ +#undef HAVE_STDDEF_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdio.h> header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strnlen' function. */ +#undef HAVE_STRNLEN + +/* Define to 1 if you have the `strsep' function. */ +#undef HAVE_STRSEP + +/* Define to 1 if you have the `strtol' function. */ +#undef HAVE_STRTOL + +/* Define to 1 if you have the `strtoul' function. */ +#undef HAVE_STRTOUL + +/* Define to 1 if `st_atim' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIM + +/* Define to 1 if `st_atimensec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMENSEC + +/* Define to 1 if `st_atimespec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMESPEC + +/* Define to 1 if `st_blocks' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_BLOCKS + +/* Define to 1 if `st_rdev' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_RDEV + +/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use + `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ +#undef HAVE_ST_BLOCKS + +/* Define to 1 if you have the `sysconf' function. */ +#undef HAVE_SYSCONF + +/* Define to 1 if you have the <syslog.h> header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the <sys/byteorder.h> header file. */ +#undef HAVE_SYS_BYTEORDER_H + +/* Define to 1 if you have the <sys/disk.h> header file. */ +#undef HAVE_SYS_DISK_H + +/* Define to 1 if you have the <sys/endian.h> header file. */ +#undef HAVE_SYS_ENDIAN_H + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the <sys/mkdev.h> header file. */ +#undef HAVE_SYS_MKDEV_H + +/* Define to 1 if you have the <sys/mount.h> header file. */ +#undef HAVE_SYS_MOUNT_H + +/* Define to 1 if you have the <sys/param.h> header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the <sys/statvfs.h> header file. */ +#undef HAVE_SYS_STATVFS_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/sysmacros.h> header file. */ +#undef HAVE_SYS_SYSMACROS_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <sys/vfs.h> header file. */ +#undef HAVE_SYS_VFS_H + +/* Define to 1 if you have the <time.h> header file. */ +#undef HAVE_TIME_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `utime' function. */ +#undef HAVE_UTIME + +/* Define to 1 if you have the `utimensat' function. */ +#undef HAVE_UTIMENSAT + +/* Define to 1 if you have the <utime.h> header file. */ +#undef HAVE_UTIME_H + +/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */ +#undef HAVE_UTIME_NULL + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if you have the <wchar.h> header file. */ +#undef HAVE_WCHAR_H + +/* Define to 1 if you have the <windows.h> header file. */ +#undef HAVE_WINDOWS_H + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Don't update /etc/mtab */ +#undef IGNORE_MTAB + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#undef LSTAT_FOLLOWS_SLASHED_SYMLINK + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Don't use default IO ops */ +#undef NO_NTFS_DEVICE_DEFAULT_IO_OPS + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* POSIX ACL support */ +#undef POSIXACLS + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Version number of package */ +#undef VERSION + +/* Define to 1 if this is a Windows OS */ +#undef WINDOWS + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Define to 1 if your processor stores words with the least significant byte + first (like Intel and VAX, unlike Motorola and SPARC). */ +#undef WORDS_LITTLEENDIAN + +/* system extended attributes mappings */ +#undef XATTR_MAPPINGS + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Required define if using POSIX threads */ +#undef _REENTRANT + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to `long int' if <sys/types.h> does not define. */ +#undef off_t + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +#undef size_t diff --git a/config.log b/config.log new file mode 100755 index 0000000000000000000000000000000000000000..56e061c4652c68f2a09a6a861cf548c9a3d53e6f --- /dev/null +++ b/config.log @@ -0,0 +1,4275 @@ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by ntfs-3g configure 2015.3.14, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ ./configure + +## --------- ## +## Platform. ## +## --------- ## + +hostname = cmc-HP-Compaq-Pro-6380-MT +uname -m = x86_64 +uname -r = 3.13.11.11 +uname -s = Linux +uname -v = #1 SMP Fri Dec 19 15:56:19 CST 2014 + +/usr/bin/uname -p = unknown +/bin/uname -X = unknown + +/bin/arch = unknown +/usr/bin/arch -k = unknown +/usr/convex/getsysinfo = unknown +/usr/bin/hostinfo = unknown +/bin/machine = unknown +/usr/bin/oslevel = unknown +/bin/universe = unknown + +PATH: /opt/qemu/bin +PATH: /home/cmc/share/chromium/depot_tools +PATH: /home/cmc/opt/linux-upgrade-tool +PATH: /home/cmc/opt/android-sdk-linux/platform-tools +PATH: /home/cmc/opt/android-sdk-linux/tools +PATH: /home/cmc/bin +PATH: /usr/local/sbin +PATH: /usr/local/bin +PATH: /usr/sbin +PATH: /usr/bin +PATH: /sbin +PATH: /bin +PATH: /usr/games +PATH: /usr/local/games + + +## ----------- ## +## Core tests. ## +## ----------- ## + +configure:2502: checking build system type +configure:2516: result: x86_64-unknown-linux-gnu +configure:2536: checking host system type +configure:2549: result: x86_64-unknown-linux-gnu +configure:2569: checking target system type +configure:2582: result: x86_64-unknown-linux-gnu +configure:2626: checking for a BSD-compatible install +configure:2694: result: /usr/bin/install -c +configure:2705: checking whether build environment is sane +configure:2760: result: yes +configure:2911: checking for a thread-safe mkdir -p +configure:2950: result: /bin/mkdir -p +configure:2957: checking for gawk +configure:2973: found /usr/bin/gawk +configure:2984: result: gawk +configure:2995: checking whether make sets $(MAKE) +configure:3017: result: yes +configure:3046: checking whether make supports nested variables +configure:3063: result: yes +configure:3193: checking whether to enable maintainer-specific portions of Makefiles +configure:3202: result: no +configure:3402: checking for style of include used by make +configure:3430: result: GNU +configure:3501: checking for gcc +configure:3517: found /usr/bin/gcc +configure:3528: result: gcc +configure:3757: checking for C compiler version +configure:3766: gcc --version >&5 +gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 +Copyright (C) 2013 Free Software Foundation, Inc. +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +configure:3777: $? = 0 +configure:3766: gcc -v >&5 +Using built-in specs. +COLLECT_GCC=gcc +COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper +Target: x86_64-linux-gnu +Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.2-19ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu +Thread model: posix +gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) +configure:3777: $? = 0 +configure:3766: gcc -V >&5 +gcc: error: unrecognized command line option '-V' +gcc: fatal error: no input files +compilation terminated. +configure:3777: $? = 4 +configure:3766: gcc -qversion >&5 +gcc: error: unrecognized command line option '-qversion' +gcc: fatal error: no input files +compilation terminated. +configure:3777: $? = 4 +configure:3797: checking whether the C compiler works +configure:3819: gcc conftest.c >&5 +configure:3823: $? = 0 +configure:3871: result: yes +configure:3874: checking for C compiler default output file name +configure:3876: result: a.out +configure:3882: checking for suffix of executables +configure:3889: gcc -o conftest conftest.c >&5 +configure:3893: $? = 0 +configure:3915: result: +configure:3937: checking whether we are cross compiling +configure:3945: gcc -o conftest conftest.c >&5 +configure:3949: $? = 0 +configure:3956: ./conftest +configure:3960: $? = 0 +configure:3975: result: no +configure:3980: checking for suffix of object files +configure:4002: gcc -c conftest.c >&5 +configure:4006: $? = 0 +configure:4027: result: o +configure:4031: checking whether we are using the GNU C compiler +configure:4050: gcc -c conftest.c >&5 +configure:4050: $? = 0 +configure:4059: result: yes +configure:4068: checking whether gcc accepts -g +configure:4088: gcc -c -g conftest.c >&5 +configure:4088: $? = 0 +configure:4129: result: yes +configure:4146: checking for gcc option to accept ISO C89 +configure:4209: gcc -c -g -O2 conftest.c >&5 +configure:4209: $? = 0 +configure:4222: result: none needed +configure:4247: checking whether gcc understands -c and -o together +configure:4269: gcc -c conftest.c -o conftest2.o +configure:4272: $? = 0 +configure:4269: gcc -c conftest.c -o conftest2.o +configure:4272: $? = 0 +configure:4284: result: yes +configure:4303: checking dependency style of gcc +configure:4414: result: gcc3 +configure:4435: checking how to run the C preprocessor +configure:4466: gcc -E conftest.c +configure:4466: $? = 0 +configure:4480: gcc -E conftest.c +conftest.c:11:28: fatal error: ac_nonexistent.h: No such file or directory + #include <ac_nonexistent.h> + ^ +compilation terminated. +configure:4480: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| /* end confdefs.h. */ +| #include <ac_nonexistent.h> +configure:4505: result: gcc -E +configure:4525: gcc -E conftest.c +configure:4525: $? = 0 +configure:4539: gcc -E conftest.c +conftest.c:11:28: fatal error: ac_nonexistent.h: No such file or directory + #include <ac_nonexistent.h> + ^ +compilation terminated. +configure:4539: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| /* end confdefs.h. */ +| #include <ac_nonexistent.h> +configure:4568: checking for grep that handles long lines and -e +configure:4626: result: /bin/grep +configure:4631: checking for egrep +configure:4693: result: /bin/grep -E +configure:4698: checking for ANSI C header files +configure:4718: gcc -c -g -O2 conftest.c >&5 +configure:4718: $? = 0 +configure:4791: gcc -o conftest -g -O2 conftest.c >&5 +configure:4791: $? = 0 +configure:4791: ./conftest +configure:4791: $? = 0 +configure:4802: result: yes +configure:4815: checking for sys/types.h +configure:4815: gcc -c -g -O2 conftest.c >&5 +configure:4815: $? = 0 +configure:4815: result: yes +configure:4815: checking for sys/stat.h +configure:4815: gcc -c -g -O2 conftest.c >&5 +configure:4815: $? = 0 +configure:4815: result: yes +configure:4815: checking for stdlib.h +configure:4815: gcc -c -g -O2 conftest.c >&5 +configure:4815: $? = 0 +configure:4815: result: yes +configure:4815: checking for string.h +configure:4815: gcc -c -g -O2 conftest.c >&5 +configure:4815: $? = 0 +configure:4815: result: yes +configure:4815: checking for memory.h +configure:4815: gcc -c -g -O2 conftest.c >&5 +configure:4815: $? = 0 +configure:4815: result: yes +configure:4815: checking for strings.h +configure:4815: gcc -c -g -O2 conftest.c >&5 +configure:4815: $? = 0 +configure:4815: result: yes +configure:4815: checking for inttypes.h +configure:4815: gcc -c -g -O2 conftest.c >&5 +configure:4815: $? = 0 +configure:4815: result: yes +configure:4815: checking for stdint.h +configure:4815: gcc -c -g -O2 conftest.c >&5 +configure:4815: $? = 0 +configure:4815: result: yes +configure:4815: checking for unistd.h +configure:4815: gcc -c -g -O2 conftest.c >&5 +configure:4815: $? = 0 +configure:4815: result: yes +configure:4828: checking minix/config.h usability +configure:4828: gcc -c -g -O2 conftest.c >&5 +conftest.c:54:26: fatal error: minix/config.h: No such file or directory + #include <minix/config.h> + ^ +compilation terminated. +configure:4828: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| #include <minix/config.h> +configure:4828: result: no +configure:4828: checking minix/config.h presence +configure:4828: gcc -E conftest.c +conftest.c:21:26: fatal error: minix/config.h: No such file or directory + #include <minix/config.h> + ^ +compilation terminated. +configure:4828: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| /* end confdefs.h. */ +| #include <minix/config.h> +configure:4828: result: no +configure:4828: checking for minix/config.h +configure:4828: result: no +configure:4849: checking whether it is safe to define __EXTENSIONS__ +configure:4867: gcc -c -g -O2 conftest.c >&5 +configure:4867: $? = 0 +configure:4874: result: yes +configure:5003: checking for C compiler version +configure:5012: gcc --version >&5 +gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 +Copyright (C) 2013 Free Software Foundation, Inc. +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +configure:5023: $? = 0 +configure:5012: gcc -v >&5 +Using built-in specs. +COLLECT_GCC=gcc +COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper +Target: x86_64-linux-gnu +Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.2-19ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu +Thread model: posix +gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) +configure:5023: $? = 0 +configure:5012: gcc -V >&5 +gcc: error: unrecognized command line option '-V' +gcc: fatal error: no input files +compilation terminated. +configure:5023: $? = 4 +configure:5012: gcc -qversion >&5 +gcc: error: unrecognized command line option '-qversion' +gcc: fatal error: no input files +compilation terminated. +configure:5023: $? = 4 +configure:5027: checking whether we are using the GNU C compiler +configure:5055: result: yes +configure:5064: checking whether gcc accepts -g +configure:5125: result: yes +configure:5142: checking for gcc option to accept ISO C89 +configure:5218: result: none needed +configure:5243: checking whether gcc understands -c and -o together +configure:5280: result: yes +configure:5299: checking dependency style of gcc +configure:5410: result: gcc3 +configure:5425: checking whether ln -s works +configure:5429: result: yes +configure:5484: checking how to print strings +configure:5511: result: printf +configure:5532: checking for a sed that does not truncate output +configure:5596: result: /bin/sed +configure:5614: checking for fgrep +configure:5676: result: /bin/grep -F +configure:5711: checking for ld used by gcc +configure:5778: result: /usr/bin/ld +configure:5785: checking if the linker (/usr/bin/ld) is GNU ld +configure:5800: result: yes +configure:5812: checking for BSD- or MS-compatible name lister (nm) +configure:5861: result: /usr/bin/nm -B +configure:5991: checking the name lister (/usr/bin/nm -B) interface +configure:5998: gcc -c -g -O2 conftest.c >&5 +configure:6001: /usr/bin/nm -B "conftest.o" +configure:6004: output +0000000000000000 B some_variable +configure:6011: result: BSD nm +configure:6015: checking the maximum length of command line arguments +configure:6145: result: 1572864 +configure:6162: checking whether the shell understands some XSI constructs +configure:6172: result: yes +configure:6176: checking whether the shell understands "+=" +configure:6182: result: yes +configure:6217: checking how to convert x86_64-unknown-linux-gnu file names to x86_64-unknown-linux-gnu format +configure:6257: result: func_convert_file_noop +configure:6264: checking how to convert x86_64-unknown-linux-gnu file names to toolchain format +configure:6284: result: func_convert_file_noop +configure:6291: checking for /usr/bin/ld option to reload object files +configure:6298: result: -r +configure:6372: checking for objdump +configure:6388: found /usr/bin/objdump +configure:6399: result: objdump +configure:6431: checking how to recognize dependent libraries +configure:6633: result: pass_all +configure:6718: checking for dlltool +configure:6748: result: no +configure:6778: checking how to associate runtime and link libraries +configure:6805: result: printf %s\n +configure:6865: checking for ar +configure:6881: found /usr/bin/ar +configure:6892: result: ar +configure:6929: checking for archiver @FILE support +configure:6946: gcc -c -g -O2 conftest.c >&5 +configure:6946: $? = 0 +configure:6949: ar cru libconftest.a @conftest.lst >&5 +configure:6952: $? = 0 +configure:6957: ar cru libconftest.a @conftest.lst >&5 +ar: conftest.o: No such file or directory +configure:6960: $? = 1 +configure:6972: result: @ +configure:7030: checking for strip +configure:7046: found /usr/bin/strip +configure:7057: result: strip +configure:7129: checking for ranlib +configure:7145: found /usr/bin/ranlib +configure:7156: result: ranlib +configure:7258: checking command to parse /usr/bin/nm -B output from gcc object +configure:7378: gcc -c -g -O2 conftest.c >&5 +configure:7381: $? = 0 +configure:7385: /usr/bin/nm -B conftest.o \| sed -n -e 's/^.*[ ]\([ABCDGIRSTW][ABCDGIRSTW]*\)[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$/\1 \2 \2/p' | sed '/ __gnu_lto/d' \> conftest.nm +configure:7388: $? = 0 +configure:7454: gcc -o conftest -g -O2 conftest.c conftstm.o >&5 +configure:7457: $? = 0 +configure:7495: result: ok +configure:7532: checking for sysroot +configure:7562: result: no +configure:7639: gcc -c -g -O2 conftest.c >&5 +configure:7642: $? = 0 +configure:7824: checking for mt +configure:7840: found /bin/mt +configure:7851: result: mt +configure:7874: checking if mt is a manifest tool +configure:7880: mt '-?' +configure:7888: result: no +configure:8527: checking for dlfcn.h +configure:8527: gcc -c -g -O2 conftest.c >&5 +configure:8527: $? = 0 +configure:8527: result: yes +configure:8733: checking for objdir +configure:8748: result: .libs +configure:9019: checking if gcc supports -fno-rtti -fno-exceptions +configure:9037: gcc -c -g -O2 -fno-rtti -fno-exceptions conftest.c >&5 +cc1: warning: command line option '-fno-rtti' is valid for C++/ObjC++ but not for C [enabled by default] +configure:9041: $? = 0 +configure:9054: result: no +configure:9381: checking for gcc option to produce PIC +configure:9388: result: -fPIC -DPIC +configure:9396: checking if gcc PIC flag -fPIC -DPIC works +configure:9414: gcc -c -g -O2 -fPIC -DPIC -DPIC conftest.c >&5 +configure:9418: $? = 0 +configure:9431: result: yes +configure:9460: checking if gcc static flag -static works +configure:9488: result: yes +configure:9503: checking if gcc supports -c -o file.o +configure:9524: gcc -c -g -O2 -o out/conftest2.o conftest.c >&5 +configure:9528: $? = 0 +configure:9550: result: yes +configure:9558: checking if gcc supports -c -o file.o +configure:9605: result: yes +configure:9638: checking whether the gcc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries +configure:10791: result: yes +configure:10828: checking whether -lc should be explicitly linked in +configure:10836: gcc -c -g -O2 conftest.c >&5 +configure:10839: $? = 0 +configure:10854: gcc -shared -fPIC -DPIC conftest.o -v -Wl,-soname -Wl,conftest -o conftest 2\>\&1 \| /bin/grep -lc \>/dev/null 2\>\&1 +configure:10857: $? = 0 +configure:10871: result: no +configure:11031: checking dynamic linker characteristics +configure:11542: gcc -o conftest -g -O2 -Wl,-rpath -Wl,/foo conftest.c >&5 +configure:11542: $? = 0 +configure:11768: result: GNU/Linux ld.so +configure:11875: checking how to hardcode library paths into programs +configure:11900: result: immediate +configure:12440: checking whether stripping libraries is possible +configure:12445: result: yes +configure:12480: checking if libtool supports shared libraries +configure:12482: result: yes +configure:12485: checking whether to build shared libraries +configure:12506: result: yes +configure:12509: checking whether to build static libraries +configure:12513: result: yes +configure:12606: checking for pkg-config +configure:12624: found /usr/bin/pkg-config +configure:12636: result: /usr/bin/pkg-config +configure:12661: checking pkg-config is at least version 0.9.0 +configure:12664: result: yes +configure:12675: checking for mv +configure:12693: found /bin/mv +configure:12705: result: /bin/mv +configure:12715: checking for rm +configure:12733: found /bin/rm +configure:12745: result: /bin/rm +configure:12755: checking for sed +configure:12785: result: /bin/sed +configure:12796: checking for ldconfig +configure:12814: found /sbin/ldconfig +configure:12827: result: /sbin/ldconfig +configure:12837: checking Windows OS +configure:12849: result: no +configure:12856: checking fuse compatibility +configure:12880: result: internal +configure:12910: checking for pthread_create in -lpthread +configure:12935: gcc -o conftest -g -O2 conftest.c -lpthread >&5 +configure:12935: $? = 0 +configure:12944: result: yes +configure:12961: checking Solaris OS +configure:12978: gcc -c -g -O2 conftest.c >&5 +conftest.c:32:6: error: #error "Not a Solaris system." + #error "Not a Solaris system." + ^ +configure:12978: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| /* end confdefs.h. */ +| +| #if !((defined(sun) || defined(__sun)) && (defined(__SVR4) || defined(__svr4__))) +| #error "Not a Solaris system." +| #endif +| +| +configure:12987: result: no +configure:13488: checking uuid/uuid.h usability +configure:13488: gcc -c -g -O2 conftest.c >&5 +conftest.c:63:23: fatal error: uuid/uuid.h: No such file or directory + #include <uuid/uuid.h> + ^ +compilation terminated. +configure:13488: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| #include <uuid/uuid.h> +configure:13488: result: no +configure:13488: checking uuid/uuid.h presence +configure:13488: gcc -E conftest.c +conftest.c:30:23: fatal error: uuid/uuid.h: No such file or directory + #include <uuid/uuid.h> + ^ +compilation terminated. +configure:13488: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| /* end confdefs.h. */ +| #include <uuid/uuid.h> +configure:13488: result: no +configure:13488: checking for uuid/uuid.h +configure:13488: result: no +configure:13493: WARNING: ntfsprogs DCE compliant UUID generation code requires the uuid library. +configure:13598: checking hd.h usability +configure:13598: gcc -c -g -O2 conftest.c >&5 +conftest.c:63:16: fatal error: hd.h: No such file or directory + #include <hd.h> + ^ +compilation terminated. +configure:13598: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| #include <hd.h> +configure:13598: result: no +configure:13598: checking hd.h presence +configure:13598: gcc -E conftest.c +conftest.c:30:16: fatal error: hd.h: No such file or directory + #include <hd.h> + ^ +compilation terminated. +configure:13598: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| /* end confdefs.h. */ +| #include <hd.h> +configure:13598: result: no +configure:13598: checking for hd.h +configure:13598: result: no +configure:13648: WARNING: ntfsprogs Windows compliant geometry code requires the hd library. +configure:13656: checking for ANSI C header files +configure:13760: result: yes +configure:13778: checking ctype.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking ctype.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for ctype.h +configure:13778: result: yes +configure:13778: checking fcntl.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking fcntl.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for fcntl.h +configure:13778: result: yes +configure:13778: checking libgen.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking libgen.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for libgen.h +configure:13778: result: yes +configure:13778: checking libintl.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking libintl.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for libintl.h +configure:13778: result: yes +configure:13778: checking limits.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking limits.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for limits.h +configure:13778: result: yes +configure:13778: checking locale.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking locale.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for locale.h +configure:13778: result: yes +configure:13778: checking mntent.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking mntent.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for mntent.h +configure:13778: result: yes +configure:13778: checking stddef.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking stddef.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for stddef.h +configure:13778: result: yes +configure:13778: checking for stdint.h +configure:13778: result: yes +configure:13778: checking for stdlib.h +configure:13778: result: yes +configure:13778: checking stdio.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking stdio.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for stdio.h +configure:13778: result: yes +configure:13778: checking stdarg.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking stdarg.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for stdarg.h +configure:13778: result: yes +configure:13778: checking for string.h +configure:13778: result: yes +configure:13778: checking for strings.h +configure:13778: result: yes +configure:13778: checking errno.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking errno.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for errno.h +configure:13778: result: yes +configure:13778: checking time.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking time.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for time.h +configure:13778: result: yes +configure:13778: checking for unistd.h +configure:13778: result: yes +configure:13778: checking utime.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking utime.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for utime.h +configure:13778: result: yes +configure:13778: checking wchar.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking wchar.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for wchar.h +configure:13778: result: yes +configure:13778: checking getopt.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking getopt.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for getopt.h +configure:13778: result: yes +configure:13778: checking features.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking features.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for features.h +configure:13778: result: yes +configure:13778: checking regex.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking regex.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for regex.h +configure:13778: result: yes +configure:13778: checking endian.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking endian.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for endian.h +configure:13778: result: yes +configure:13778: checking byteswap.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking byteswap.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for byteswap.h +configure:13778: result: yes +configure:13778: checking sys/byteorder.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +conftest.c:88:27: fatal error: sys/byteorder.h: No such file or directory + #include <sys/byteorder.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| #include <sys/byteorder.h> +configure:13778: result: no +configure:13778: checking sys/byteorder.h presence +configure:13778: gcc -E conftest.c +conftest.c:55:27: fatal error: sys/byteorder.h: No such file or directory + #include <sys/byteorder.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| /* end confdefs.h. */ +| #include <sys/byteorder.h> +configure:13778: result: no +configure:13778: checking for sys/byteorder.h +configure:13778: result: no +configure:13778: checking sys/disk.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +conftest.c:88:22: fatal error: sys/disk.h: No such file or directory + #include <sys/disk.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| #include <sys/disk.h> +configure:13778: result: no +configure:13778: checking sys/disk.h presence +configure:13778: gcc -E conftest.c +conftest.c:55:22: fatal error: sys/disk.h: No such file or directory + #include <sys/disk.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| /* end confdefs.h. */ +| #include <sys/disk.h> +configure:13778: result: no +configure:13778: checking for sys/disk.h +configure:13778: result: no +configure:13778: checking sys/endian.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +conftest.c:88:24: fatal error: sys/endian.h: No such file or directory + #include <sys/endian.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| #include <sys/endian.h> +configure:13778: result: no +configure:13778: checking sys/endian.h presence +configure:13778: gcc -E conftest.c +conftest.c:55:24: fatal error: sys/endian.h: No such file or directory + #include <sys/endian.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| /* end confdefs.h. */ +| #include <sys/endian.h> +configure:13778: result: no +configure:13778: checking for sys/endian.h +configure:13778: result: no +configure:13778: checking sys/param.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking sys/param.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for sys/param.h +configure:13778: result: yes +configure:13778: checking sys/ioctl.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking sys/ioctl.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for sys/ioctl.h +configure:13778: result: yes +configure:13778: checking sys/mkdev.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +conftest.c:90:23: fatal error: sys/mkdev.h: No such file or directory + #include <sys/mkdev.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| #include <sys/mkdev.h> +configure:13778: result: no +configure:13778: checking sys/mkdev.h presence +configure:13778: gcc -E conftest.c +conftest.c:57:23: fatal error: sys/mkdev.h: No such file or directory + #include <sys/mkdev.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| /* end confdefs.h. */ +| #include <sys/mkdev.h> +configure:13778: result: no +configure:13778: checking for sys/mkdev.h +configure:13778: result: no +configure:13778: checking sys/mount.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking sys/mount.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for sys/mount.h +configure:13778: result: yes +configure:13778: checking for sys/stat.h +configure:13778: result: yes +configure:13778: checking for sys/types.h +configure:13778: result: yes +configure:13778: checking sys/vfs.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking sys/vfs.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for sys/vfs.h +configure:13778: result: yes +configure:13778: checking sys/statvfs.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking sys/statvfs.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for sys/statvfs.h +configure:13778: result: yes +configure:13778: checking sys/sysmacros.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking sys/sysmacros.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for sys/sysmacros.h +configure:13778: result: yes +configure:13778: checking linux/major.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking linux/major.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for linux/major.h +configure:13778: result: yes +configure:13778: checking linux/fd.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking linux/fd.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for linux/fd.h +configure:13778: result: yes +configure:13778: checking linux/fs.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking linux/fs.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for linux/fs.h +configure:13778: result: yes +configure:13778: checking for inttypes.h +configure:13778: result: yes +configure:13778: checking linux/hdreg.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking linux/hdreg.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for linux/hdreg.h +configure:13778: result: yes +configure:13778: checking machine/endian.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +conftest.c:101:28: fatal error: machine/endian.h: No such file or directory + #include <machine/endian.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| #include <machine/endian.h> +configure:13778: result: no +configure:13778: checking machine/endian.h presence +configure:13778: gcc -E conftest.c +conftest.c:68:28: fatal error: machine/endian.h: No such file or directory + #include <machine/endian.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| /* end confdefs.h. */ +| #include <machine/endian.h> +configure:13778: result: no +configure:13778: checking for machine/endian.h +configure:13778: result: no +configure:13778: checking windows.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +conftest.c:101:21: fatal error: windows.h: No such file or directory + #include <windows.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| #include <windows.h> +configure:13778: result: no +configure:13778: checking windows.h presence +configure:13778: gcc -E conftest.c +conftest.c:68:21: fatal error: windows.h: No such file or directory + #include <windows.h> + ^ +compilation terminated. +configure:13778: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| /* end confdefs.h. */ +| #include <windows.h> +configure:13778: result: no +configure:13778: checking for windows.h +configure:13778: result: no +configure:13778: checking syslog.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking syslog.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for syslog.h +configure:13778: result: yes +configure:13778: checking pwd.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking pwd.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for pwd.h +configure:13778: result: yes +configure:13778: checking malloc.h usability +configure:13778: gcc -c -g -O2 conftest.c >&5 +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking malloc.h presence +configure:13778: gcc -E conftest.c +configure:13778: $? = 0 +configure:13778: result: yes +configure:13778: checking for malloc.h +configure:13778: result: yes +configure:13790: checking for stdbool.h that conforms to C99 +configure:13857: gcc -c -g -O2 conftest.c >&5 +configure:13857: $? = 0 +configure:13864: result: yes +configure:13866: checking for _Bool +configure:13866: gcc -c -g -O2 conftest.c >&5 +configure:13866: $? = 0 +configure:13866: gcc -c -g -O2 conftest.c >&5 +conftest.c: In function 'main': +conftest.c:107:20: error: expected expression before ')' token + if (sizeof ((_Bool))) + ^ +configure:13866: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| #define HAVE_SYSLOG_H 1 +| #define HAVE_PWD_H 1 +| #define HAVE_MALLOC_H 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| int +| main () +| { +| if (sizeof ((_Bool))) +| return 0; +| ; +| return 0; +| } +configure:13866: result: yes +configure:13883: checking whether byte ordering is bigendian +configure:13898: gcc -c -g -O2 conftest.c >&5 +conftest.c:74:9: error: unknown type name 'not' + not a universal capable compiler + ^ +conftest.c:74:15: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'universal' + not a universal capable compiler + ^ +conftest.c:74:15: error: unknown type name 'universal' +configure:13898: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| #define HAVE_SYSLOG_H 1 +| #define HAVE_PWD_H 1 +| #define HAVE_MALLOC_H 1 +| #define HAVE__BOOL 1 +| #define HAVE_STDBOOL_H 1 +| /* end confdefs.h. */ +| #ifndef __APPLE_CC__ +| not a universal capable compiler +| #endif +| typedef int dummy; +| +configure:13943: gcc -c -g -O2 conftest.c >&5 +configure:13943: $? = 0 +configure:13961: gcc -c -g -O2 conftest.c >&5 +conftest.c: In function 'main': +conftest.c:80:4: error: unknown type name 'not' + not big endian + ^ +conftest.c:80:12: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'endian' + not big endian + ^ +configure:13961: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| #define HAVE_SYSLOG_H 1 +| #define HAVE_PWD_H 1 +| #define HAVE_MALLOC_H 1 +| #define HAVE__BOOL 1 +| #define HAVE_STDBOOL_H 1 +| /* end confdefs.h. */ +| #include <sys/types.h> +| #include <sys/param.h> +| +| int +| main () +| { +| #if BYTE_ORDER != BIG_ENDIAN +| not big endian +| #endif +| +| ; +| return 0; +| } +configure:14089: result: no +configure:14112: checking for an ANSI C-conforming const +configure:14178: gcc -c -g -O2 conftest.c >&5 +configure:14178: $? = 0 +configure:14185: result: yes +configure:14193: checking for inline +configure:14209: gcc -c -g -O2 conftest.c >&5 +configure:14209: $? = 0 +configure:14217: result: inline +configure:14235: checking for off_t +configure:14235: gcc -c -g -O2 conftest.c >&5 +configure:14235: $? = 0 +configure:14235: gcc -c -g -O2 conftest.c >&5 +conftest.c: In function 'main': +conftest.c:110:20: error: expected expression before ')' token + if (sizeof ((off_t))) + ^ +configure:14235: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| #define HAVE_SYSLOG_H 1 +| #define HAVE_PWD_H 1 +| #define HAVE_MALLOC_H 1 +| #define HAVE__BOOL 1 +| #define HAVE_STDBOOL_H 1 +| #define WORDS_LITTLEENDIAN 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| int +| main () +| { +| if (sizeof ((off_t))) +| return 0; +| ; +| return 0; +| } +configure:14235: result: yes +configure:14246: checking for size_t +configure:14246: gcc -c -g -O2 conftest.c >&5 +configure:14246: $? = 0 +configure:14246: gcc -c -g -O2 conftest.c >&5 +conftest.c: In function 'main': +conftest.c:110:21: error: expected expression before ')' token + if (sizeof ((size_t))) + ^ +configure:14246: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| #define HAVE_SYSLOG_H 1 +| #define HAVE_PWD_H 1 +| #define HAVE_MALLOC_H 1 +| #define HAVE__BOOL 1 +| #define HAVE_STDBOOL_H 1 +| #define WORDS_LITTLEENDIAN 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| int +| main () +| { +| if (sizeof ((size_t))) +| return 0; +| ; +| return 0; +| } +configure:14246: result: yes +configure:14257: checking for struct stat.st_blocks +configure:14257: gcc -c -g -O2 conftest.c >&5 +configure:14257: $? = 0 +configure:14257: result: yes +configure:14277: checking for struct stat.st_rdev +configure:14277: gcc -c -g -O2 conftest.c >&5 +configure:14277: $? = 0 +configure:14277: result: yes +configure:14287: checking for struct stat.st_atim +configure:14287: gcc -c -g -O2 conftest.c >&5 +conftest.c: In function 'main': +conftest.c:114:5: error: used struct type value where scalar is required + if (ac_aggr.st_atim) + ^ +configure:14287: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| #define HAVE_SYSLOG_H 1 +| #define HAVE_PWD_H 1 +| #define HAVE_MALLOC_H 1 +| #define HAVE__BOOL 1 +| #define HAVE_STDBOOL_H 1 +| #define WORDS_LITTLEENDIAN 1 +| #define HAVE_STRUCT_STAT_ST_BLOCKS 1 +| #define HAVE_ST_BLOCKS 1 +| #define HAVE_STRUCT_STAT_ST_RDEV 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| int +| main () +| { +| static struct stat ac_aggr; +| if (ac_aggr.st_atim) +| return 0; +| ; +| return 0; +| } +configure:14287: gcc -c -g -O2 conftest.c >&5 +configure:14287: $? = 0 +configure:14287: result: yes +configure:14297: checking for struct stat.st_atimespec +configure:14297: gcc -c -g -O2 conftest.c >&5 +conftest.c: In function 'main': +conftest.c:115:12: error: 'struct stat' has no member named 'st_atimespec' + if (ac_aggr.st_atimespec) + ^ +configure:14297: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| #define HAVE_SYSLOG_H 1 +| #define HAVE_PWD_H 1 +| #define HAVE_MALLOC_H 1 +| #define HAVE__BOOL 1 +| #define HAVE_STDBOOL_H 1 +| #define WORDS_LITTLEENDIAN 1 +| #define HAVE_STRUCT_STAT_ST_BLOCKS 1 +| #define HAVE_ST_BLOCKS 1 +| #define HAVE_STRUCT_STAT_ST_RDEV 1 +| #define HAVE_STRUCT_STAT_ST_ATIM 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| int +| main () +| { +| static struct stat ac_aggr; +| if (ac_aggr.st_atimespec) +| return 0; +| ; +| return 0; +| } +configure:14297: gcc -c -g -O2 conftest.c >&5 +conftest.c: In function 'main': +conftest.c:115:19: error: 'struct stat' has no member named 'st_atimespec' + if (sizeof ac_aggr.st_atimespec) + ^ +configure:14297: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| #define HAVE_SYSLOG_H 1 +| #define HAVE_PWD_H 1 +| #define HAVE_MALLOC_H 1 +| #define HAVE__BOOL 1 +| #define HAVE_STDBOOL_H 1 +| #define WORDS_LITTLEENDIAN 1 +| #define HAVE_STRUCT_STAT_ST_BLOCKS 1 +| #define HAVE_ST_BLOCKS 1 +| #define HAVE_STRUCT_STAT_ST_RDEV 1 +| #define HAVE_STRUCT_STAT_ST_ATIM 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| int +| main () +| { +| static struct stat ac_aggr; +| if (sizeof ac_aggr.st_atimespec) +| return 0; +| ; +| return 0; +| } +configure:14297: result: no +configure:14307: checking for struct stat.st_atimensec +configure:14307: gcc -c -g -O2 conftest.c >&5 +conftest.c: In function 'main': +conftest.c:115:12: error: 'struct stat' has no member named 'st_atimensec' + if (ac_aggr.st_atimensec) + ^ +configure:14307: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| #define HAVE_SYSLOG_H 1 +| #define HAVE_PWD_H 1 +| #define HAVE_MALLOC_H 1 +| #define HAVE__BOOL 1 +| #define HAVE_STDBOOL_H 1 +| #define WORDS_LITTLEENDIAN 1 +| #define HAVE_STRUCT_STAT_ST_BLOCKS 1 +| #define HAVE_ST_BLOCKS 1 +| #define HAVE_STRUCT_STAT_ST_RDEV 1 +| #define HAVE_STRUCT_STAT_ST_ATIM 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| int +| main () +| { +| static struct stat ac_aggr; +| if (ac_aggr.st_atimensec) +| return 0; +| ; +| return 0; +| } +configure:14307: gcc -c -g -O2 conftest.c >&5 +conftest.c: In function 'main': +conftest.c:115:19: error: 'struct stat' has no member named 'st_atimensec' + if (sizeof ac_aggr.st_atimensec) + ^ +configure:14307: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| #define HAVE_SYSLOG_H 1 +| #define HAVE_PWD_H 1 +| #define HAVE_MALLOC_H 1 +| #define HAVE__BOOL 1 +| #define HAVE_STDBOOL_H 1 +| #define WORDS_LITTLEENDIAN 1 +| #define HAVE_STRUCT_STAT_ST_BLOCKS 1 +| #define HAVE_ST_BLOCKS 1 +| #define HAVE_STRUCT_STAT_ST_RDEV 1 +| #define HAVE_STRUCT_STAT_ST_ATIM 1 +| /* end confdefs.h. */ +| #include <stdio.h> +| #ifdef HAVE_SYS_TYPES_H +| # include <sys/types.h> +| #endif +| #ifdef HAVE_SYS_STAT_H +| # include <sys/stat.h> +| #endif +| #ifdef STDC_HEADERS +| # include <stdlib.h> +| # include <stddef.h> +| #else +| # ifdef HAVE_STDLIB_H +| # include <stdlib.h> +| # endif +| #endif +| #ifdef HAVE_STRING_H +| # if !defined STDC_HEADERS && defined HAVE_MEMORY_H +| # include <memory.h> +| # endif +| # include <string.h> +| #endif +| #ifdef HAVE_STRINGS_H +| # include <strings.h> +| #endif +| #ifdef HAVE_INTTYPES_H +| # include <inttypes.h> +| #endif +| #ifdef HAVE_STDINT_H +| # include <stdint.h> +| #endif +| #ifdef HAVE_UNISTD_H +| # include <unistd.h> +| #endif +| int +| main () +| { +| static struct stat ac_aggr; +| if (sizeof ac_aggr.st_atimensec) +| return 0; +| ; +| return 0; +| } +configure:14307: result: no +configure:14343: checking for library containing getmntent +configure:14374: gcc -o conftest -g -O2 conftest.c >&5 +configure:14374: $? = 0 +configure:14391: result: none required +configure:14406: checking whether mbrtowc and mbstate_t are properly declared +configure:14426: gcc -o conftest -g -O2 conftest.c >&5 +configure:14426: $? = 0 +configure:14434: result: yes +configure:14442: checking for working memcmp +configure:14485: gcc -o conftest -g -O2 conftest.c >&5 +configure:14485: $? = 0 +configure:14485: ./conftest +configure:14485: $? = 0 +configure:14495: result: yes +configure:14504: checking whether lstat correctly handles trailing slash +configure:14530: gcc -o conftest -g -O2 conftest.c >&5 +configure:14530: $? = 0 +configure:14530: ./conftest +configure:14530: $? = 0 +configure:14547: result: yes +configure:14566: checking whether stat accepts an empty string +configure:14586: gcc -o conftest -g -O2 conftest.c >&5 +configure:14586: $? = 0 +configure:14586: ./conftest +configure:14586: $? = 0 +configure:14596: result: no +configure:14614: checking for strftime +configure:14614: gcc -o conftest -g -O2 conftest.c >&5 +conftest.c:104:6: warning: conflicting types for built-in function 'strftime' [enabled by default] + char strftime (); + ^ +configure:14614: $? = 0 +configure:14614: result: yes +configure:14673: checking for utime.h +configure:14673: result: yes +configure:14687: checking whether utime accepts a null argument +configure:14716: gcc -o conftest -g -O2 conftest.c >&5 +configure:14716: $? = 0 +configure:14716: ./conftest +configure:14716: $? = 0 +configure:14726: result: yes +configure:14738: checking for vprintf +configure:14738: gcc -o conftest -g -O2 conftest.c >&5 +conftest.c:107:6: warning: conflicting types for built-in function 'vprintf' [enabled by default] + char vprintf (); + ^ +configure:14738: $? = 0 +configure:14738: result: yes +configure:14744: checking for _doprnt +configure:14744: gcc -o conftest -g -O2 conftest.c >&5 +/tmp/ccezr7Vq.o: In function `main': +/home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/conftest.c:119: undefined reference to `_doprnt' +collect2: error: ld returned 1 exit status +configure:14744: $? = 1 +configure: failed program was: +| /* confdefs.h */ +| #define PACKAGE_NAME "ntfs-3g" +| #define PACKAGE_TARNAME "ntfs-3g" +| #define PACKAGE_VERSION "2015.3.14" +| #define PACKAGE_STRING "ntfs-3g 2015.3.14" +| #define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +| #define PACKAGE_URL "" +| #define PACKAGE "ntfs-3g" +| #define VERSION "2015.3.14" +| #define STDC_HEADERS 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_MEMORY_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_UNISTD_H 1 +| #define __EXTENSIONS__ 1 +| #define _ALL_SOURCE 1 +| #define _GNU_SOURCE 1 +| #define _POSIX_PTHREAD_SEMANTICS 1 +| #define _TANDEM_SOURCE 1 +| #define HAVE_DLFCN_H 1 +| #define LT_OBJDIR ".libs/" +| #define _REENTRANT 1 +| #define FUSE_INTERNAL 1 +| #define STDC_HEADERS 1 +| #define HAVE_CTYPE_H 1 +| #define HAVE_FCNTL_H 1 +| #define HAVE_LIBGEN_H 1 +| #define HAVE_LIBINTL_H 1 +| #define HAVE_LIMITS_H 1 +| #define HAVE_LOCALE_H 1 +| #define HAVE_MNTENT_H 1 +| #define HAVE_STDDEF_H 1 +| #define HAVE_STDINT_H 1 +| #define HAVE_STDLIB_H 1 +| #define HAVE_STDIO_H 1 +| #define HAVE_STDARG_H 1 +| #define HAVE_STRING_H 1 +| #define HAVE_STRINGS_H 1 +| #define HAVE_ERRNO_H 1 +| #define HAVE_TIME_H 1 +| #define HAVE_UNISTD_H 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_WCHAR_H 1 +| #define HAVE_GETOPT_H 1 +| #define HAVE_FEATURES_H 1 +| #define HAVE_REGEX_H 1 +| #define HAVE_ENDIAN_H 1 +| #define HAVE_BYTESWAP_H 1 +| #define HAVE_SYS_PARAM_H 1 +| #define HAVE_SYS_IOCTL_H 1 +| #define HAVE_SYS_MOUNT_H 1 +| #define HAVE_SYS_STAT_H 1 +| #define HAVE_SYS_TYPES_H 1 +| #define HAVE_SYS_VFS_H 1 +| #define HAVE_SYS_STATVFS_H 1 +| #define HAVE_SYS_SYSMACROS_H 1 +| #define HAVE_LINUX_MAJOR_H 1 +| #define HAVE_LINUX_FD_H 1 +| #define HAVE_LINUX_FS_H 1 +| #define HAVE_INTTYPES_H 1 +| #define HAVE_LINUX_HDREG_H 1 +| #define HAVE_SYSLOG_H 1 +| #define HAVE_PWD_H 1 +| #define HAVE_MALLOC_H 1 +| #define HAVE__BOOL 1 +| #define HAVE_STDBOOL_H 1 +| #define WORDS_LITTLEENDIAN 1 +| #define HAVE_STRUCT_STAT_ST_BLOCKS 1 +| #define HAVE_ST_BLOCKS 1 +| #define HAVE_STRUCT_STAT_ST_RDEV 1 +| #define HAVE_STRUCT_STAT_ST_ATIM 1 +| #define HAVE_GETMNTENT 1 +| #define HAVE_MBRTOWC 1 +| #define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 +| #define HAVE_STRFTIME 1 +| #define HAVE_UTIME_H 1 +| #define HAVE_UTIME_NULL 1 +| #define HAVE_VPRINTF 1 +| /* end confdefs.h. */ +| /* Define _doprnt to an innocuous variant, in case <limits.h> declares _doprnt. +| For example, HP-UX 11i <limits.h> declares gettimeofday. */ +| #define _doprnt innocuous__doprnt +| +| /* System header to define __stub macros and hopefully few prototypes, +| which can conflict with char _doprnt (); below. +| Prefer <limits.h> to <assert.h> if __STDC__ is defined, since +| <limits.h> exists even on freestanding compilers. */ +| +| #ifdef __STDC__ +| # include <limits.h> +| #else +| # include <assert.h> +| #endif +| +| #undef _doprnt +| +| /* Override any GCC internal prototype to avoid an error. +| Use char because int might match the return type of a GCC +| builtin and then its argument prototype would still apply. */ +| #ifdef __cplusplus +| extern "C" +| #endif +| char _doprnt (); +| /* The GNU C library defines this for functions which it implements +| to always fail with ENOSYS. Some functions are actually named +| something starting with __ and the normal name is an alias. */ +| #if defined __stub__doprnt || defined __stub____doprnt +| choke me +| #endif +| +| int +| main () +| { +| return _doprnt (); +| ; +| return 0; +| } +configure:14744: result: no +configure:14763: checking for atexit +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for basename +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for daemon +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for dup2 +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for fdatasync +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for ffs +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +conftest.c:113:6: warning: conflicting types for built-in function 'ffs' [enabled by default] + char ffs (); + ^ +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for getopt_long +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for hasmntopt +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for mbsinit +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for memmove +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +conftest.c:117:6: warning: conflicting types for built-in function 'memmove' [enabled by default] + char memmove (); + ^ +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for memset +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +conftest.c:118:6: warning: conflicting types for built-in function 'memset' [enabled by default] + char memset (); + ^ +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for realpath +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for regcomp +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for setlocale +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for setxattr +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for strcasecmp +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +conftest.c:123:6: warning: conflicting types for built-in function 'strcasecmp' [enabled by default] + char strcasecmp (); + ^ +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for strchr +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +conftest.c:124:6: warning: conflicting types for built-in function 'strchr' [enabled by default] + char strchr (); + ^ +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for strdup +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +conftest.c:125:6: warning: conflicting types for built-in function 'strdup' [enabled by default] + char strdup (); + ^ +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for strerror +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for strnlen +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for strsep +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for strtol +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for strtoul +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for sysconf +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for utime +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for utimensat +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for gettimeofday +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for clock_gettime +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for fork +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +conftest.c:136:6: warning: conflicting types for built-in function 'fork' [enabled by default] + char fork (); + ^ +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for memcpy +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +conftest.c:137:6: warning: conflicting types for built-in function 'memcpy' [enabled by default] + char memcpy (); + ^ +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for random +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +configure:14763: $? = 0 +configure:14763: result: yes +configure:14763: checking for snprintf +configure:14763: gcc -o conftest -g -O2 conftest.c >&5 +conftest.c:139:6: warning: conflicting types for built-in function 'snprintf' [enabled by default] + char snprintf (); + ^ +configure:14763: $? = 0 +configure:14763: result: yes +configure:14779: checking for special C compiler options needed for large files +configure:14824: result: no +configure:14830: checking for _FILE_OFFSET_BITS value needed for large files +configure:14855: gcc -c -g -O2 conftest.c >&5 +configure:14855: $? = 0 +configure:14887: result: no +configure:15266: checking that generated files are newer than configure +configure:15272: result: done +configure:15356: creating ./config.status + +## ---------------------- ## +## Running config.status. ## +## ---------------------- ## + +This file was extended by ntfs-3g config.status 2015.3.14, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = + CONFIG_HEADERS = + CONFIG_LINKS = + CONFIG_COMMANDS = + $ ./config.status + +on cmc-HP-Compaq-Pro-6380-MT + +config.status:1266: creating Makefile +config.status:1266: creating include/Makefile +config.status:1266: creating include/fuse-lite/Makefile +config.status:1266: creating include/ntfs-3g/Makefile +config.status:1266: creating libfuse-lite/Makefile +config.status:1266: creating libntfs-3g/Makefile +config.status:1266: creating libntfs-3g/libntfs-3g.pc +config.status:1266: creating libntfs-3g/libntfs-3g.script.so +config.status:1266: creating ntfsprogs/Makefile +config.status:1266: creating ntfsprogs/mkntfs.8 +config.status:1266: creating ntfsprogs/ntfscat.8 +config.status:1266: creating ntfsprogs/ntfsclone.8 +config.status:1266: creating ntfsprogs/ntfscluster.8 +config.status:1266: creating ntfsprogs/ntfscmp.8 +config.status:1266: creating ntfsprogs/ntfscp.8 +config.status:1266: creating ntfsprogs/ntfsfix.8 +config.status:1266: creating ntfsprogs/ntfsinfo.8 +config.status:1266: creating ntfsprogs/ntfslabel.8 +config.status:1266: creating ntfsprogs/ntfsls.8 +config.status:1266: creating ntfsprogs/ntfsprogs.8 +config.status:1266: creating ntfsprogs/ntfsresize.8 +config.status:1266: creating ntfsprogs/ntfsundelete.8 +config.status:1266: creating ntfsprogs/ntfsdecrypt.8 +config.status:1266: creating ntfsprogs/ntfswipe.8 +config.status:1266: creating ntfsprogs/ntfstruncate.8 +config.status:1266: creating ntfsprogs/ntfsfallocate.8 +config.status:1266: creating src/Makefile +config.status:1266: creating src/ntfs-3g.8 +config.status:1266: creating src/ntfs-3g.probe.8 +config.status:1266: creating src/ntfs-3g.usermap.8 +config.status:1266: creating src/ntfs-3g.secaudit.8 +config.status:1266: creating config.h +config.status:1495: executing depfiles commands +config.status:1495: executing libtool commands + +## ---------------- ## +## Cache variables. ## +## ---------------- ## + +ac_cv_build=x86_64-unknown-linux-gnu +ac_cv_c_bigendian=no +ac_cv_c_compiler_gnu=yes +ac_cv_c_const=yes +ac_cv_c_inline=inline +ac_cv_env_CC_set= +ac_cv_env_CC_value= +ac_cv_env_CFLAGS_set= +ac_cv_env_CFLAGS_value= +ac_cv_env_CPPFLAGS_set= +ac_cv_env_CPPFLAGS_value= +ac_cv_env_CPP_set= +ac_cv_env_CPP_value= +ac_cv_env_FUSE_MODULE_CFLAGS_set= +ac_cv_env_FUSE_MODULE_CFLAGS_value= +ac_cv_env_FUSE_MODULE_LIBS_set= +ac_cv_env_FUSE_MODULE_LIBS_value= +ac_cv_env_GNUTLS_CFLAGS_set= +ac_cv_env_GNUTLS_CFLAGS_value= +ac_cv_env_GNUTLS_LIBS_set= +ac_cv_env_GNUTLS_LIBS_value= +ac_cv_env_LDCONFIG_set= +ac_cv_env_LDCONFIG_value= +ac_cv_env_LDFLAGS_set= +ac_cv_env_LDFLAGS_value= +ac_cv_env_LIBS_set= +ac_cv_env_LIBS_value= +ac_cv_env_PKG_CONFIG_LIBDIR_set= +ac_cv_env_PKG_CONFIG_LIBDIR_value= +ac_cv_env_PKG_CONFIG_PATH_set= +ac_cv_env_PKG_CONFIG_PATH_value= +ac_cv_env_PKG_CONFIG_set= +ac_cv_env_PKG_CONFIG_value= +ac_cv_env_build_alias_set= +ac_cv_env_build_alias_value= +ac_cv_env_host_alias_set= +ac_cv_env_host_alias_value= +ac_cv_env_target_alias_set= +ac_cv_env_target_alias_value= +ac_cv_func__doprnt=no +ac_cv_func_atexit=yes +ac_cv_func_basename=yes +ac_cv_func_clock_gettime=yes +ac_cv_func_daemon=yes +ac_cv_func_dup2=yes +ac_cv_func_fdatasync=yes +ac_cv_func_ffs=yes +ac_cv_func_fork=yes +ac_cv_func_getmntent=yes +ac_cv_func_getopt_long=yes +ac_cv_func_gettimeofday=yes +ac_cv_func_hasmntopt=yes +ac_cv_func_lstat_dereferences_slashed_symlink=yes +ac_cv_func_mbrtowc=yes +ac_cv_func_mbsinit=yes +ac_cv_func_memcmp_working=yes +ac_cv_func_memcpy=yes +ac_cv_func_memmove=yes +ac_cv_func_memset=yes +ac_cv_func_random=yes +ac_cv_func_realpath=yes +ac_cv_func_regcomp=yes +ac_cv_func_setlocale=yes +ac_cv_func_setxattr=yes +ac_cv_func_snprintf=yes +ac_cv_func_stat_empty_string_bug=no +ac_cv_func_strcasecmp=yes +ac_cv_func_strchr=yes +ac_cv_func_strdup=yes +ac_cv_func_strerror=yes +ac_cv_func_strftime=yes +ac_cv_func_strnlen=yes +ac_cv_func_strsep=yes +ac_cv_func_strtol=yes +ac_cv_func_strtoul=yes +ac_cv_func_sysconf=yes +ac_cv_func_utime=yes +ac_cv_func_utime_null=yes +ac_cv_func_utimensat=yes +ac_cv_func_vprintf=yes +ac_cv_header_byteswap_h=yes +ac_cv_header_ctype_h=yes +ac_cv_header_dlfcn_h=yes +ac_cv_header_endian_h=yes +ac_cv_header_errno_h=yes +ac_cv_header_fcntl_h=yes +ac_cv_header_features_h=yes +ac_cv_header_getopt_h=yes +ac_cv_header_hd_h=no +ac_cv_header_inttypes_h=yes +ac_cv_header_libgen_h=yes +ac_cv_header_libintl_h=yes +ac_cv_header_limits_h=yes +ac_cv_header_linux_fd_h=yes +ac_cv_header_linux_fs_h=yes +ac_cv_header_linux_hdreg_h=yes +ac_cv_header_linux_major_h=yes +ac_cv_header_locale_h=yes +ac_cv_header_machine_endian_h=no +ac_cv_header_malloc_h=yes +ac_cv_header_memory_h=yes +ac_cv_header_minix_config_h=no +ac_cv_header_mntent_h=yes +ac_cv_header_pwd_h=yes +ac_cv_header_regex_h=yes +ac_cv_header_stdarg_h=yes +ac_cv_header_stdbool_h=yes +ac_cv_header_stdc=yes +ac_cv_header_stddef_h=yes +ac_cv_header_stdint_h=yes +ac_cv_header_stdio_h=yes +ac_cv_header_stdlib_h=yes +ac_cv_header_string_h=yes +ac_cv_header_strings_h=yes +ac_cv_header_sys_byteorder_h=no +ac_cv_header_sys_disk_h=no +ac_cv_header_sys_endian_h=no +ac_cv_header_sys_ioctl_h=yes +ac_cv_header_sys_mkdev_h=no +ac_cv_header_sys_mount_h=yes +ac_cv_header_sys_param_h=yes +ac_cv_header_sys_stat_h=yes +ac_cv_header_sys_statvfs_h=yes +ac_cv_header_sys_sysmacros_h=yes +ac_cv_header_sys_types_h=yes +ac_cv_header_sys_vfs_h=yes +ac_cv_header_syslog_h=yes +ac_cv_header_time_h=yes +ac_cv_header_unistd_h=yes +ac_cv_header_utime_h=yes +ac_cv_header_uuid_uuid_h=no +ac_cv_header_wchar_h=yes +ac_cv_header_windows_h=no +ac_cv_host=x86_64-unknown-linux-gnu +ac_cv_lib_pthread_pthread_create=yes +ac_cv_member_struct_stat_st_atim=yes +ac_cv_member_struct_stat_st_atimensec=no +ac_cv_member_struct_stat_st_atimespec=no +ac_cv_member_struct_stat_st_blocks=yes +ac_cv_member_struct_stat_st_rdev=yes +ac_cv_objext=o +ac_cv_path_EGREP='/bin/grep -E' +ac_cv_path_FGREP='/bin/grep -F' +ac_cv_path_GREP=/bin/grep +ac_cv_path_LDCONFIG=/sbin/ldconfig +ac_cv_path_MV=/bin/mv +ac_cv_path_RM=/bin/rm +ac_cv_path_SED=/bin/sed +ac_cv_path_ac_pt_PKG_CONFIG=/usr/bin/pkg-config +ac_cv_path_install='/usr/bin/install -c' +ac_cv_path_mkdir=/bin/mkdir +ac_cv_prog_AWK=gawk +ac_cv_prog_CPP='gcc -E' +ac_cv_prog_ac_ct_AR=ar +ac_cv_prog_ac_ct_CC=gcc +ac_cv_prog_ac_ct_MANIFEST_TOOL=mt +ac_cv_prog_ac_ct_OBJDUMP=objdump +ac_cv_prog_ac_ct_RANLIB=ranlib +ac_cv_prog_ac_ct_STRIP=strip +ac_cv_prog_cc_c89= +ac_cv_prog_cc_g=yes +ac_cv_prog_make_make_set=yes +ac_cv_safe_to_define___extensions__=yes +ac_cv_search_getmntent='none required' +ac_cv_sys_file_offset_bits=no +ac_cv_sys_largefile_CC=no +ac_cv_target=x86_64-unknown-linux-gnu +ac_cv_type__Bool=yes +ac_cv_type_off_t=yes +ac_cv_type_size_t=yes +am_cv_CC_dependencies_compiler_type=gcc3 +am_cv_make_support_nested_variables=yes +am_cv_prog_cc_c_o=yes +lt_cv_ar_at_file=@ +lt_cv_archive_cmds_need_lc=no +lt_cv_deplibs_check_method=pass_all +lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_ld_reload_flag=-r +lt_cv_nm_interface='BSD nm' +lt_cv_objdir=.libs +lt_cv_path_LD=/usr/bin/ld +lt_cv_path_NM='/usr/bin/nm -B' +lt_cv_path_mainfest_tool=no +lt_cv_prog_compiler_c_o=yes +lt_cv_prog_compiler_pic='-fPIC -DPIC' +lt_cv_prog_compiler_pic_works=yes +lt_cv_prog_compiler_rtti_exceptions=no +lt_cv_prog_compiler_static_works=yes +lt_cv_prog_gnu_ld=yes +lt_cv_sharedlib_from_linklib_cmd='printf %s\n' +lt_cv_shlibpath_overrides_runpath=no +lt_cv_sys_global_symbol_pipe='sed -n -e '\''s/^.*[ ]\([ABCDGIRSTW][ABCDGIRSTW]*\)[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$/\1 \2 \2/p'\'' | sed '\''/ __gnu_lto/d'\''' +lt_cv_sys_global_symbol_to_c_name_address='sed -n -e '\''s/^: \([^ ]*\)[ ]*$/ {\"\1\", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \([^ ]*\)$/ {"\2", (void *) \&\2},/p'\''' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='sed -n -e '\''s/^: \([^ ]*\)[ ]*$/ {\"\1\", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \(lib[^ ]*\)$/ {"\2", (void *) \&\2},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \([^ ]*\)$/ {"lib\2", (void *) \&\2},/p'\''' +lt_cv_sys_global_symbol_to_cdecl='sed -n -e '\''s/^T .* \(.*\)$/extern int \1();/p'\'' -e '\''s/^[ABCDGIRSTW]* .* \(.*\)$/extern char \1;/p'\''' +lt_cv_sys_max_cmd_len=1572864 +lt_cv_to_host_file_cmd=func_convert_file_noop +lt_cv_to_tool_file_cmd=func_convert_file_noop + +## ----------------- ## +## Output variables. ## +## ----------------- ## + +ACLOCAL='${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing aclocal-1.14' +AMDEPBACKSLASH='\' +AMDEP_FALSE='#' +AMDEP_TRUE='' +AMTAR='$${TAR-tar}' +AM_BACKSLASH='\' +AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +AM_DEFAULT_VERBOSITY='1' +AM_V='$(V)' +AR='ar' +AUTOCONF='${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoconf' +AUTOHEADER='${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoheader' +AUTOMAKE='${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing automake-1.14' +AWK='gawk' +CC='gcc' +CCDEPMODE='depmode=gcc3' +CFLAGS='-g -O2 -Wall' +CPP='gcc -E' +CPPFLAGS='' +CYGPATH_W='echo' +DEFS='-DHAVE_CONFIG_H' +DEPDIR='.deps' +DLLTOOL='false' +DSYMUTIL='' +DUMPBIN='' +ECHO_C='' +ECHO_N='-n' +ECHO_T='' +EGREP='/bin/grep -E' +ENABLE_CRYPTO_FALSE='' +ENABLE_CRYPTO_TRUE='#' +ENABLE_EXTRAS_FALSE='' +ENABLE_EXTRAS_TRUE='#' +ENABLE_MOUNT_HELPER_FALSE='#' +ENABLE_MOUNT_HELPER_TRUE='' +ENABLE_NTFSPROGS_FALSE='#' +ENABLE_NTFSPROGS_TRUE='' +ENABLE_NTFS_3G_FALSE='#' +ENABLE_NTFS_3G_TRUE='' +ENABLE_QUARANTINED_FALSE='' +ENABLE_QUARANTINED_TRUE='#' +EXEEXT='' +FGREP='/bin/grep -F' +FUSE_INTERNAL_FALSE='#' +FUSE_INTERNAL_TRUE='' +FUSE_MODULE_CFLAGS='' +FUSE_MODULE_LIBS='' +GENERATE_LDSCRIPT_FALSE='' +GENERATE_LDSCRIPT_TRUE='#' +GNUTLS_CFLAGS='' +GNUTLS_LIBS='' +GREP='/bin/grep' +INSTALL_DATA='${INSTALL} -m 644' +INSTALL_LIBRARY_FALSE='#' +INSTALL_LIBRARY_TRUE='' +INSTALL_PROGRAM='${INSTALL}' +INSTALL_SCRIPT='${INSTALL}' +INSTALL_STRIP_PROGRAM='$(install_sh) -c -s' +LD='/usr/bin/ld -m elf_x86_64' +LDCONFIG='/sbin/ldconfig' +LDFLAGS='' +LIBFUSE_LITE_CFLAGS='' +LIBFUSE_LITE_LIBS=' -lpthread' +LIBGCRYPT_CFLAGS='' +LIBGCRYPT_CONFIG='' +LIBGCRYPT_LIBS='' +LIBNTFS_3G_VERSION='86' +LIBNTFS_CPPFLAGS='' +LIBNTFS_LIBS='' +LIBOBJS='' +LIBS='' +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +LIPO='' +LN_S='ln -s' +LTLIBOBJS='' +MAINT='#' +MAINTAINER_MODE_FALSE='' +MAINTAINER_MODE_TRUE='#' +MAKEINFO='${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing makeinfo' +MANIFEST_TOOL=':' +MKDIR_P='/bin/mkdir -p' +MKNTFS_CPPFLAGS='' +MKNTFS_LIBS='' +MV='/bin/mv' +NM='/usr/bin/nm -B' +NMEDIT='' +NTFSPROGS_STATIC_LIBS='' +NTFS_DEVICE_DEFAULT_IO_OPS_FALSE='#' +NTFS_DEVICE_DEFAULT_IO_OPS_TRUE='' +OBJDUMP='objdump' +OBJEXT='o' +OTOOL64='' +OTOOL='' +OUTPUT_FORMAT='' +PACKAGE='ntfs-3g' +PACKAGE_BUGREPORT='ntfs-3g-devel@lists.sf.net' +PACKAGE_NAME='ntfs-3g' +PACKAGE_STRING='ntfs-3g 2015.3.14' +PACKAGE_TARNAME='ntfs-3g' +PACKAGE_URL='' +PACKAGE_VERSION='2015.3.14' +PATH_SEPARATOR=':' +PKG_CONFIG='/usr/bin/pkg-config' +PKG_CONFIG_LIBDIR='' +PKG_CONFIG_PATH='' +RANLIB='ranlib' +REALLYSTATIC_FALSE='' +REALLYSTATIC_TRUE='#' +RM='/bin/rm' +RUN_LDCONFIG_FALSE='#' +RUN_LDCONFIG_TRUE='' +SED='/bin/sed' +SET_MAKE='' +SHELL='/bin/bash' +STRIP='strip' +VERSION='2015.3.14' +WINDOWS_FALSE='' +WINDOWS_TRUE='#' +ac_ct_AR='ar' +ac_ct_CC='gcc' +ac_ct_DUMPBIN='' +all_includes=' ' +all_libraries=' ' +am__EXEEXT_FALSE='' +am__EXEEXT_TRUE='#' +am__fastdepCC_FALSE='#' +am__fastdepCC_TRUE='' +am__include='include' +am__isrc='' +am__leading_dot='.' +am__nodep='_no' +am__quote='' +am__tar='$${TAR-tar} chof - "$$tardir"' +am__untar='$${TAR-tar} xf -' +bindir='${exec_prefix}/bin' +build='x86_64-unknown-linux-gnu' +build_alias='' +build_cpu='x86_64' +build_os='linux-gnu' +build_vendor='unknown' +datadir='${datarootdir}' +datarootdir='${prefix}/share' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +dvidir='${docdir}' +exec_prefix='${prefix}' +host='x86_64-unknown-linux-gnu' +host_alias='' +host_cpu='x86_64' +host_os='linux-gnu' +host_vendor='unknown' +htmldir='${docdir}' +includedir='${prefix}/include' +infodir='${datarootdir}/info' +install_sh='${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/install-sh' +libdir='${exec_prefix}/lib' +libexecdir='${exec_prefix}/libexec' +localedir='${datarootdir}/locale' +localstatedir='${prefix}/var' +mandir='${datarootdir}/man' +mkdir_p='$(MKDIR_P)' +ntfs3gincludedir='$(includedir)/ntfs-3g' +oldincludedir='/usr/include' +pdfdir='${docdir}' +pkgconfigdir='$(libdir)/pkgconfig' +prefix='/usr/local' +program_transform_name='s,x,x,' +psdir='${docdir}' +rootbindir='/bin' +rootlibdir='/lib' +rootsbindir='/sbin' +sbindir='${exec_prefix}/sbin' +sharedstatedir='${prefix}/com' +sysconfdir='${prefix}/etc' +target='x86_64-unknown-linux-gnu' +target_alias='' +target_cpu='x86_64' +target_os='linux-gnu' +target_vendor='unknown' + +## ----------- ## +## confdefs.h. ## +## ----------- ## + +/* confdefs.h */ +#define PACKAGE_NAME "ntfs-3g" +#define PACKAGE_TARNAME "ntfs-3g" +#define PACKAGE_VERSION "2015.3.14" +#define PACKAGE_STRING "ntfs-3g 2015.3.14" +#define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net" +#define PACKAGE_URL "" +#define PACKAGE "ntfs-3g" +#define VERSION "2015.3.14" +#define STDC_HEADERS 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRING_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_UNISTD_H 1 +#define __EXTENSIONS__ 1 +#define _ALL_SOURCE 1 +#define _GNU_SOURCE 1 +#define _POSIX_PTHREAD_SEMANTICS 1 +#define _TANDEM_SOURCE 1 +#define HAVE_DLFCN_H 1 +#define LT_OBJDIR ".libs/" +#define _REENTRANT 1 +#define FUSE_INTERNAL 1 +#define STDC_HEADERS 1 +#define HAVE_CTYPE_H 1 +#define HAVE_FCNTL_H 1 +#define HAVE_LIBGEN_H 1 +#define HAVE_LIBINTL_H 1 +#define HAVE_LIMITS_H 1 +#define HAVE_LOCALE_H 1 +#define HAVE_MNTENT_H 1 +#define HAVE_STDDEF_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STDIO_H 1 +#define HAVE_STDARG_H 1 +#define HAVE_STRING_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_ERRNO_H 1 +#define HAVE_TIME_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_UTIME_H 1 +#define HAVE_WCHAR_H 1 +#define HAVE_GETOPT_H 1 +#define HAVE_FEATURES_H 1 +#define HAVE_REGEX_H 1 +#define HAVE_ENDIAN_H 1 +#define HAVE_BYTESWAP_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_IOCTL_H 1 +#define HAVE_SYS_MOUNT_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_VFS_H 1 +#define HAVE_SYS_STATVFS_H 1 +#define HAVE_SYS_SYSMACROS_H 1 +#define HAVE_LINUX_MAJOR_H 1 +#define HAVE_LINUX_FD_H 1 +#define HAVE_LINUX_FS_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_LINUX_HDREG_H 1 +#define HAVE_SYSLOG_H 1 +#define HAVE_PWD_H 1 +#define HAVE_MALLOC_H 1 +#define HAVE__BOOL 1 +#define HAVE_STDBOOL_H 1 +#define WORDS_LITTLEENDIAN 1 +#define HAVE_STRUCT_STAT_ST_BLOCKS 1 +#define HAVE_ST_BLOCKS 1 +#define HAVE_STRUCT_STAT_ST_RDEV 1 +#define HAVE_STRUCT_STAT_ST_ATIM 1 +#define HAVE_GETMNTENT 1 +#define HAVE_MBRTOWC 1 +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 +#define HAVE_STRFTIME 1 +#define HAVE_UTIME_H 1 +#define HAVE_UTIME_NULL 1 +#define HAVE_VPRINTF 1 +#define HAVE_ATEXIT 1 +#define HAVE_BASENAME 1 +#define HAVE_DAEMON 1 +#define HAVE_DUP2 1 +#define HAVE_FDATASYNC 1 +#define HAVE_FFS 1 +#define HAVE_GETOPT_LONG 1 +#define HAVE_HASMNTOPT 1 +#define HAVE_MBSINIT 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMSET 1 +#define HAVE_REALPATH 1 +#define HAVE_REGCOMP 1 +#define HAVE_SETLOCALE 1 +#define HAVE_SETXATTR 1 +#define HAVE_STRCASECMP 1 +#define HAVE_STRCHR 1 +#define HAVE_STRDUP 1 +#define HAVE_STRERROR 1 +#define HAVE_STRNLEN 1 +#define HAVE_STRSEP 1 +#define HAVE_STRTOL 1 +#define HAVE_STRTOUL 1 +#define HAVE_SYSCONF 1 +#define HAVE_UTIME 1 +#define HAVE_UTIMENSAT 1 +#define HAVE_GETTIMEOFDAY 1 +#define HAVE_CLOCK_GETTIME 1 +#define HAVE_FORK 1 +#define HAVE_MEMCPY 1 +#define HAVE_RANDOM 1 +#define HAVE_SNPRINTF 1 + +configure: exit 0 diff --git a/config.status b/config.status new file mode 100755 index 0000000000000000000000000000000000000000..1cf4f7350f74afe5ea6452646d361dbc85eef18b --- /dev/null +++ b/config.status @@ -0,0 +1,2230 @@ +#! /bin/bash +# Generated by configure. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=${CONFIG_SHELL-/bin/bash} +export SHELL +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by ntfs-3g $as_me 2015.3.14, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +# Files that config.status was made for. +config_files=" Makefile include/Makefile include/fuse-lite/Makefile include/ntfs-3g/Makefile libfuse-lite/Makefile libntfs-3g/Makefile libntfs-3g/libntfs-3g.pc libntfs-3g/libntfs-3g.script.so ntfsprogs/Makefile ntfsprogs/mkntfs.8 ntfsprogs/ntfscat.8 ntfsprogs/ntfsclone.8 ntfsprogs/ntfscluster.8 ntfsprogs/ntfscmp.8 ntfsprogs/ntfscp.8 ntfsprogs/ntfsfix.8 ntfsprogs/ntfsinfo.8 ntfsprogs/ntfslabel.8 ntfsprogs/ntfsls.8 ntfsprogs/ntfsprogs.8 ntfsprogs/ntfsresize.8 ntfsprogs/ntfsundelete.8 ntfsprogs/ntfsdecrypt.8 ntfsprogs/ntfswipe.8 ntfsprogs/ntfstruncate.8 ntfsprogs/ntfsfallocate.8 src/Makefile src/ntfs-3g.8 src/ntfs-3g.probe.8 src/ntfs-3g.usermap.8 src/ntfs-3g.secaudit.8" +config_headers=" config.h" +config_commands=" depfiles libtool" + +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to <ntfs-3g-devel@lists.sf.net>." + +ac_cs_config="" +ac_cs_version="\ +ntfs-3g config.status 2015.3.14 +configured by ./configure, generated by GNU Autoconf 2.69, + with options \"$ac_cs_config\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='/home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14' +srcdir='.' +INSTALL='/usr/bin/install -c' +MKDIR_P='/bin/mkdir -p' +AWK='gawk' +test -n "$AWK" || AWK=awk +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +if $ac_cs_recheck; then + set X /bin/bash './configure' $ac_configure_extra_args --no-create --no-recursion + shift + $as_echo "running CONFIG_SHELL=/bin/bash $*" >&6 + CONFIG_SHELL='/bin/bash' + export CONFIG_SHELL + exec "$@" +fi + +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +# +# INIT-COMMANDS +# +AMDEP_TRUE="" ac_aux_dir="." + + +# 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 + +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' +double_quote_subst='s/\(["`\\]\)/\\\1/g' +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' +macro_version='2.4.2' +macro_revision='1.3337' +enable_shared='yes' +enable_static='yes' +pic_mode='default' +enable_fast_install='yes' +SHELL='/bin/bash' +ECHO='printf %s\n' +PATH_SEPARATOR=':' +host_alias='' +host='x86_64-unknown-linux-gnu' +host_os='linux-gnu' +build_alias='' +build='x86_64-unknown-linux-gnu' +build_os='linux-gnu' +SED='/bin/sed' +Xsed='/bin/sed -e 1s/^X//' +GREP='/bin/grep' +EGREP='/bin/grep -E' +FGREP='/bin/grep -F' +LD='/usr/bin/ld -m elf_x86_64' +NM='/usr/bin/nm -B' +LN_S='ln -s' +max_cmd_len='1572864' +ac_objext='o' +exeext='' +lt_unset='unset' +lt_SP2NL='tr \040 \012' +lt_NL2SP='tr \015\012 \040\040' +lt_cv_to_host_file_cmd='func_convert_file_noop' +lt_cv_to_tool_file_cmd='func_convert_file_noop' +reload_flag=' -r' +reload_cmds='$LD$reload_flag -o $output$reload_objs' +OBJDUMP='objdump' +deplibs_check_method='pass_all' +file_magic_cmd='$MAGIC_CMD' +file_magic_glob='' +want_nocaseglob='no' +DLLTOOL='false' +sharedlib_from_linklib_cmd='printf %s\n' +AR='ar' +AR_FLAGS='cru' +archiver_list_spec='@' +STRIP='strip' +RANLIB='ranlib' +old_postinstall_cmds='chmod 644 $oldlib~$RANLIB $tool_oldlib' +old_postuninstall_cmds='' +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs~$RANLIB $tool_oldlib' +lock_old_archive_extraction='no' +CC='gcc' +CFLAGS='-g -O2 -Wall' +compiler='gcc' +GCC='yes' +lt_cv_sys_global_symbol_pipe='sed -n -e '\''s/^.*[ ]\([ABCDGIRSTW][ABCDGIRSTW]*\)[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$/\1 \2 \2/p'\'' | sed '\''/ __gnu_lto/d'\''' +lt_cv_sys_global_symbol_to_cdecl='sed -n -e '\''s/^T .* \(.*\)$/extern int \1();/p'\'' -e '\''s/^[ABCDGIRSTW]* .* \(.*\)$/extern char \1;/p'\''' +lt_cv_sys_global_symbol_to_c_name_address='sed -n -e '\''s/^: \([^ ]*\)[ ]*$/ {\"\1\", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \([^ ]*\)$/ {"\2", (void *) \&\2},/p'\''' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='sed -n -e '\''s/^: \([^ ]*\)[ ]*$/ {\"\1\", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \(lib[^ ]*\)$/ {"\2", (void *) \&\2},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \([^ ]*\)$/ {"lib\2", (void *) \&\2},/p'\''' +nm_file_list_spec='@' +lt_sysroot='' +objdir='.libs' +MAGIC_CMD='file' +lt_prog_compiler_no_builtin_flag=' -fno-builtin' +lt_prog_compiler_pic=' -fPIC -DPIC' +lt_prog_compiler_wl='-Wl,' +lt_prog_compiler_static='-static' +lt_cv_prog_compiler_c_o='yes' +need_locks='no' +MANIFEST_TOOL=':' +DSYMUTIL='' +NMEDIT='' +LIPO='' +OTOOL='' +OTOOL64='' +libext='a' +shrext_cmds='.so' +extract_expsyms_cmds='' +archive_cmds_need_lc='no' +enable_shared_with_static_runtimes='no' +export_dynamic_flag_spec='${wl}--export-dynamic' +whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' +compiler_needs_object='no' +old_archive_from_new_cmds='' +old_archive_from_expsyms_cmds='' +archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' +archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' +module_cmds='' +module_expsym_cmds='' +with_gnu_ld='yes' +allow_undefined_flag='' +no_undefined_flag='' +hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' +hardcode_libdir_separator='' +hardcode_direct='no' +hardcode_direct_absolute='no' +hardcode_minus_L='no' +hardcode_shlibpath_var='unsupported' +hardcode_automatic='no' +inherit_rpath='no' +link_all_deplibs='unknown' +always_export_symbols='no' +export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' +exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' +include_expsyms='' +prelink_cmds='' +postlink_cmds='' +file_list_spec='' +variables_saved_for_relink='PATH LD_LIBRARY_PATH LD_RUN_PATH GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH' +need_lib_prefix='no' +need_version='no' +version_type='linux' +runpath_var='LD_RUN_PATH' +shlibpath_var='LD_LIBRARY_PATH' +shlibpath_overrides_runpath='no' +libname_spec='lib$name' +library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' +soname_spec='${libname}${release}${shared_ext}$major' +install_override_mode='' +postinstall_cmds='' +postuninstall_cmds='' +finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' +finish_eval='' +hardcode_into_libs='yes' +sys_lib_search_path_spec='/usr/lib/gcc/x86_64-linux-gnu/4.8 /usr/lib/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib ' +sys_lib_dlsearch_path_spec='/lib64 /usr/lib64 /lib /usr/lib /usr/lib/x86_64-linux-gnu/libfakeroot /lib/i386-linux-gnu /usr/lib/i386-linux-gnu /lib/i686-linux-gnu /usr/lib/i686-linux-gnu /usr/local/lib /lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/mesa-egl /usr/lib/x86_64-linux-gnu/mesa /usr/lib/x86_64-linux-gnu/mir/clientplatform/mesa /lib32 /usr/lib32 /libx32 /usr/libx32 ' +hardcode_action='immediate' +enable_dlopen='unknown' +enable_dlopen_self='unknown' +enable_dlopen_self_static='unknown' +old_striplib='strip --strip-debug' +striplib='strip --strip-unneeded' + +LTCC='gcc' +LTCFLAGS='-g -O2' +compiler='gcc' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in SHELL ECHO PATH_SEPARATOR SED GREP EGREP FGREP LD NM LN_S lt_SP2NL lt_NL2SP reload_flag OBJDUMP deplibs_check_method file_magic_cmd file_magic_glob want_nocaseglob DLLTOOL sharedlib_from_linklib_cmd AR AR_FLAGS archiver_list_spec STRIP RANLIB CC CFLAGS compiler lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl lt_cv_sys_global_symbol_to_c_name_address lt_cv_sys_global_symbol_to_c_name_address_lib_prefix nm_file_list_spec lt_prog_compiler_no_builtin_flag lt_prog_compiler_pic lt_prog_compiler_wl lt_prog_compiler_static lt_cv_prog_compiler_c_o need_locks MANIFEST_TOOL DSYMUTIL NMEDIT LIPO OTOOL OTOOL64 shrext_cmds export_dynamic_flag_spec whole_archive_flag_spec compiler_needs_object with_gnu_ld allow_undefined_flag no_undefined_flag hardcode_libdir_flag_spec hardcode_libdir_separator exclude_expsyms include_expsyms file_list_spec variables_saved_for_relink libname_spec library_names_spec soname_spec install_override_mode finish_eval old_striplib striplib; do + case `eval \\$ECHO \\""\\$$var"\\"` in + *[\\\`\"\$]*) + eval "lt_$var=\\\"\`\$ECHO \"\$$var\" | \$SED \"\$sed_quote_subst\"\`\\\"" + ;; + *) + eval "lt_$var=\\\"\$$var\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds old_postinstall_cmds old_postuninstall_cmds old_archive_cmds extract_expsyms_cmds old_archive_from_new_cmds old_archive_from_expsyms_cmds archive_cmds archive_expsym_cmds module_cmds module_expsym_cmds export_symbols_cmds prelink_cmds postlink_cmds postinstall_cmds postuninstall_cmds finish_cmds sys_lib_search_path_spec sys_lib_dlsearch_path_spec; do + case `eval \\$ECHO \\""\\$$var"\\"` in + *[\\\`\"\$]*) + eval "lt_$var=\\\"\`\$ECHO \"\$$var\" | \$SED -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" + ;; + *) + eval "lt_$var=\\\"\$$var\\\"" + ;; + esac +done + +ac_aux_dir='.' +xsi_shell='yes' +lt_shell_append='yes' + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='ntfs-3g' + VERSION='2015.3.14' + TIMESTAMP='' + RM='/bin/rm' + ofile='libtool' + + + + + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; + "include/fuse-lite/Makefile") CONFIG_FILES="$CONFIG_FILES include/fuse-lite/Makefile" ;; + "include/ntfs-3g/Makefile") CONFIG_FILES="$CONFIG_FILES include/ntfs-3g/Makefile" ;; + "libfuse-lite/Makefile") CONFIG_FILES="$CONFIG_FILES libfuse-lite/Makefile" ;; + "libntfs-3g/Makefile") CONFIG_FILES="$CONFIG_FILES libntfs-3g/Makefile" ;; + "libntfs-3g/libntfs-3g.pc") CONFIG_FILES="$CONFIG_FILES libntfs-3g/libntfs-3g.pc" ;; + "libntfs-3g/libntfs-3g.script.so") CONFIG_FILES="$CONFIG_FILES libntfs-3g/libntfs-3g.script.so" ;; + "ntfsprogs/Makefile") CONFIG_FILES="$CONFIG_FILES ntfsprogs/Makefile" ;; + "ntfsprogs/mkntfs.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/mkntfs.8" ;; + "ntfsprogs/ntfscat.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfscat.8" ;; + "ntfsprogs/ntfsclone.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsclone.8" ;; + "ntfsprogs/ntfscluster.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfscluster.8" ;; + "ntfsprogs/ntfscmp.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfscmp.8" ;; + "ntfsprogs/ntfscp.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfscp.8" ;; + "ntfsprogs/ntfsfix.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsfix.8" ;; + "ntfsprogs/ntfsinfo.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsinfo.8" ;; + "ntfsprogs/ntfslabel.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfslabel.8" ;; + "ntfsprogs/ntfsls.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsls.8" ;; + "ntfsprogs/ntfsprogs.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsprogs.8" ;; + "ntfsprogs/ntfsresize.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsresize.8" ;; + "ntfsprogs/ntfsundelete.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsundelete.8" ;; + "ntfsprogs/ntfsdecrypt.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsdecrypt.8" ;; + "ntfsprogs/ntfswipe.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfswipe.8" ;; + "ntfsprogs/ntfstruncate.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfstruncate.8" ;; + "ntfsprogs/ntfsfallocate.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsfallocate.8" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "src/ntfs-3g.8") CONFIG_FILES="$CONFIG_FILES src/ntfs-3g.8" ;; + "src/ntfs-3g.probe.8") CONFIG_FILES="$CONFIG_FILES src/ntfs-3g.probe.8" ;; + "src/ntfs-3g.usermap.8") CONFIG_FILES="$CONFIG_FILES src/ntfs-3g.usermap.8" ;; + "src/ntfs-3g.secaudit.8") CONFIG_FILES="$CONFIG_FILES src/ntfs-3g.secaudit.8" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +cat >>"$ac_tmp/subs1.awk" <<\_ACAWK && +S["am__EXEEXT_FALSE"]="" +S["am__EXEEXT_TRUE"]="#" +S["LTLIBOBJS"]="" +S["ENABLE_QUARANTINED_FALSE"]="" +S["ENABLE_QUARANTINED_TRUE"]="#" +S["ENABLE_EXTRAS_FALSE"]="" +S["ENABLE_EXTRAS_TRUE"]="#" +S["ENABLE_NTFSPROGS_FALSE"]="#" +S["ENABLE_NTFSPROGS_TRUE"]="" +S["ENABLE_NTFS_3G_FALSE"]="#" +S["ENABLE_NTFS_3G_TRUE"]="" +S["ENABLE_MOUNT_HELPER_FALSE"]="#" +S["ENABLE_MOUNT_HELPER_TRUE"]="" +S["INSTALL_LIBRARY_FALSE"]="#" +S["INSTALL_LIBRARY_TRUE"]="" +S["REALLYSTATIC_FALSE"]="" +S["REALLYSTATIC_TRUE"]="#" +S["RUN_LDCONFIG_FALSE"]="#" +S["RUN_LDCONFIG_TRUE"]="" +S["NTFS_DEVICE_DEFAULT_IO_OPS_FALSE"]="#" +S["NTFS_DEVICE_DEFAULT_IO_OPS_TRUE"]="" +S["WINDOWS_FALSE"]="" +S["WINDOWS_TRUE"]="#" +S["GENERATE_LDSCRIPT_FALSE"]="" +S["GENERATE_LDSCRIPT_TRUE"]="#" +S["FUSE_INTERNAL_FALSE"]="#" +S["FUSE_INTERNAL_TRUE"]="" +S["OUTPUT_FORMAT"]="" +S["NTFSPROGS_STATIC_LIBS"]="" +S["LIBNTFS_LIBS"]="" +S["LIBNTFS_CPPFLAGS"]="" +S["MKNTFS_LIBS"]="" +S["MKNTFS_CPPFLAGS"]="" +S["LIBFUSE_LITE_LIBS"]=" -lpthread" +S["LIBFUSE_LITE_CFLAGS"]="" +S["LIBNTFS_3G_VERSION"]="86" +S["rootlibdir"]="/lib" +S["rootsbindir"]="/sbin" +S["rootbindir"]="/bin" +S["ntfs3gincludedir"]="$(includedir)/ntfs-3g" +S["pkgconfigdir"]="$(libdir)/pkgconfig" +S["LIBOBJS"]="" +S["all_libraries"]=" " +S["all_includes"]=" " +S["ENABLE_CRYPTO_FALSE"]="" +S["ENABLE_CRYPTO_TRUE"]="#" +S["GNUTLS_LIBS"]="" +S["GNUTLS_CFLAGS"]="" +S["LIBGCRYPT_LIBS"]="" +S["LIBGCRYPT_CFLAGS"]="" +S["LIBGCRYPT_CONFIG"]="" +S["FUSE_MODULE_LIBS"]="" +S["FUSE_MODULE_CFLAGS"]="" +S["LDCONFIG"]="/sbin/ldconfig" +S["RM"]="/bin/rm" +S["MV"]="/bin/mv" +S["PKG_CONFIG_LIBDIR"]="" +S["PKG_CONFIG_PATH"]="" +S["PKG_CONFIG"]="/usr/bin/pkg-config" +S["OTOOL64"]="" +S["OTOOL"]="" +S["LIPO"]="" +S["NMEDIT"]="" +S["DSYMUTIL"]="" +S["MANIFEST_TOOL"]=":" +S["RANLIB"]="ranlib" +S["ac_ct_AR"]="ar" +S["AR"]="ar" +S["DLLTOOL"]="false" +S["OBJDUMP"]="objdump" +S["NM"]="/usr/bin/nm -B" +S["ac_ct_DUMPBIN"]="" +S["DUMPBIN"]="" +S["LD"]="/usr/bin/ld -m elf_x86_64" +S["FGREP"]="/bin/grep -F" +S["SED"]="/bin/sed" +S["LIBTOOL"]="$(SHELL) $(top_builddir)/libtool" +S["LN_S"]="ln -s" +S["EGREP"]="/bin/grep -E" +S["GREP"]="/bin/grep" +S["CPP"]="gcc -E" +S["am__fastdepCC_FALSE"]="#" +S["am__fastdepCC_TRUE"]="" +S["CCDEPMODE"]="depmode=gcc3" +S["am__nodep"]="_no" +S["AMDEPBACKSLASH"]="\\" +S["AMDEP_FALSE"]="#" +S["AMDEP_TRUE"]="" +S["am__quote"]="" +S["am__include"]="include" +S["DEPDIR"]=".deps" +S["OBJEXT"]="o" +S["EXEEXT"]="" +S["ac_ct_CC"]="gcc" +S["CPPFLAGS"]="" +S["LDFLAGS"]="" +S["CFLAGS"]="-g -O2 -Wall" +S["CC"]="gcc" +S["MAINT"]="#" +S["MAINTAINER_MODE_FALSE"]="" +S["MAINTAINER_MODE_TRUE"]="#" +S["AM_BACKSLASH"]="\\" +S["AM_DEFAULT_VERBOSITY"]="1" +S["AM_DEFAULT_V"]="$(AM_DEFAULT_VERBOSITY)" +S["AM_V"]="$(V)" +S["am__untar"]="$${TAR-tar} xf -" +S["am__tar"]="$${TAR-tar} chof - \"$$tardir\"" +S["AMTAR"]="$${TAR-tar}" +S["am__leading_dot"]="." +S["SET_MAKE"]="" +S["AWK"]="gawk" +S["mkdir_p"]="$(MKDIR_P)" +S["MKDIR_P"]="/bin/mkdir -p" +S["INSTALL_STRIP_PROGRAM"]="$(install_sh) -c -s" +S["STRIP"]="strip" +S["install_sh"]="${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/install-sh" +S["MAKEINFO"]="${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing makeinfo" +S["AUTOHEADER"]="${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoheader" +S["AUTOMAKE"]="${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing automake-1.14" +S["AUTOCONF"]="${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoconf" +S["ACLOCAL"]="${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing aclocal-1.14" +S["VERSION"]="2015.3.14" +S["PACKAGE"]="ntfs-3g" +S["CYGPATH_W"]="echo" +S["am__isrc"]="" +S["INSTALL_DATA"]="${INSTALL} -m 644" +S["INSTALL_SCRIPT"]="${INSTALL}" +S["INSTALL_PROGRAM"]="${INSTALL}" +S["target_os"]="linux-gnu" +S["target_vendor"]="unknown" +S["target_cpu"]="x86_64" +S["target"]="x86_64-unknown-linux-gnu" +S["host_os"]="linux-gnu" +S["host_vendor"]="unknown" +S["host_cpu"]="x86_64" +S["host"]="x86_64-unknown-linux-gnu" +S["build_os"]="linux-gnu" +S["build_vendor"]="unknown" +S["build_cpu"]="x86_64" +S["build"]="x86_64-unknown-linux-gnu" +S["target_alias"]="" +S["host_alias"]="" +S["build_alias"]="" +S["LIBS"]="" +S["ECHO_T"]="" +S["ECHO_N"]="-n" +S["ECHO_C"]="" +S["DEFS"]="-DHAVE_CONFIG_H" +S["mandir"]="${datarootdir}/man" +S["localedir"]="${datarootdir}/locale" +S["libdir"]="${exec_prefix}/lib" +S["psdir"]="${docdir}" +S["pdfdir"]="${docdir}" +S["dvidir"]="${docdir}" +S["htmldir"]="${docdir}" +S["infodir"]="${datarootdir}/info" +S["docdir"]="${datarootdir}/doc/${PACKAGE_TARNAME}" +S["oldincludedir"]="/usr/include" +S["includedir"]="${prefix}/include" +S["localstatedir"]="${prefix}/var" +S["sharedstatedir"]="${prefix}/com" +S["sysconfdir"]="${prefix}/etc" +S["datadir"]="${datarootdir}" +S["datarootdir"]="${prefix}/share" +S["libexecdir"]="${exec_prefix}/libexec" +S["sbindir"]="${exec_prefix}/sbin" +S["bindir"]="${exec_prefix}/bin" +S["program_transform_name"]="s,x,x," +S["prefix"]="/usr/local" +S["exec_prefix"]="${prefix}" +S["PACKAGE_URL"]="" +S["PACKAGE_BUGREPORT"]="ntfs-3g-devel@lists.sf.net" +S["PACKAGE_STRING"]="ntfs-3g 2015.3.14" +S["PACKAGE_VERSION"]="2015.3.14" +S["PACKAGE_TARNAME"]="ntfs-3g" +S["PACKAGE_NAME"]="ntfs-3g" +S["PATH_SEPARATOR"]=":" +S["SHELL"]="/bin/bash" +_ACAWK +cat >>"$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +D["PACKAGE_NAME"]=" \"ntfs-3g\"" +D["PACKAGE_TARNAME"]=" \"ntfs-3g\"" +D["PACKAGE_VERSION"]=" \"2015.3.14\"" +D["PACKAGE_STRING"]=" \"ntfs-3g 2015.3.14\"" +D["PACKAGE_BUGREPORT"]=" \"ntfs-3g-devel@lists.sf.net\"" +D["PACKAGE_URL"]=" \"\"" +D["PACKAGE"]=" \"ntfs-3g\"" +D["VERSION"]=" \"2015.3.14\"" +D["STDC_HEADERS"]=" 1" +D["HAVE_SYS_TYPES_H"]=" 1" +D["HAVE_SYS_STAT_H"]=" 1" +D["HAVE_STDLIB_H"]=" 1" +D["HAVE_STRING_H"]=" 1" +D["HAVE_MEMORY_H"]=" 1" +D["HAVE_STRINGS_H"]=" 1" +D["HAVE_INTTYPES_H"]=" 1" +D["HAVE_STDINT_H"]=" 1" +D["HAVE_UNISTD_H"]=" 1" +D["__EXTENSIONS__"]=" 1" +D["_ALL_SOURCE"]=" 1" +D["_GNU_SOURCE"]=" 1" +D["_POSIX_PTHREAD_SEMANTICS"]=" 1" +D["_TANDEM_SOURCE"]=" 1" +D["HAVE_DLFCN_H"]=" 1" +D["LT_OBJDIR"]=" \".libs/\"" +D["_REENTRANT"]=" 1" +D["FUSE_INTERNAL"]=" 1" +D["STDC_HEADERS"]=" 1" +D["HAVE_CTYPE_H"]=" 1" +D["HAVE_FCNTL_H"]=" 1" +D["HAVE_LIBGEN_H"]=" 1" +D["HAVE_LIBINTL_H"]=" 1" +D["HAVE_LIMITS_H"]=" 1" +D["HAVE_LOCALE_H"]=" 1" +D["HAVE_MNTENT_H"]=" 1" +D["HAVE_STDDEF_H"]=" 1" +D["HAVE_STDINT_H"]=" 1" +D["HAVE_STDLIB_H"]=" 1" +D["HAVE_STDIO_H"]=" 1" +D["HAVE_STDARG_H"]=" 1" +D["HAVE_STRING_H"]=" 1" +D["HAVE_STRINGS_H"]=" 1" +D["HAVE_ERRNO_H"]=" 1" +D["HAVE_TIME_H"]=" 1" +D["HAVE_UNISTD_H"]=" 1" +D["HAVE_UTIME_H"]=" 1" +D["HAVE_WCHAR_H"]=" 1" +D["HAVE_GETOPT_H"]=" 1" +D["HAVE_FEATURES_H"]=" 1" +D["HAVE_REGEX_H"]=" 1" +D["HAVE_ENDIAN_H"]=" 1" +D["HAVE_BYTESWAP_H"]=" 1" +D["HAVE_SYS_PARAM_H"]=" 1" +D["HAVE_SYS_IOCTL_H"]=" 1" +D["HAVE_SYS_MOUNT_H"]=" 1" +D["HAVE_SYS_STAT_H"]=" 1" +D["HAVE_SYS_TYPES_H"]=" 1" +D["HAVE_SYS_VFS_H"]=" 1" +D["HAVE_SYS_STATVFS_H"]=" 1" +D["HAVE_SYS_SYSMACROS_H"]=" 1" +D["HAVE_LINUX_MAJOR_H"]=" 1" +D["HAVE_LINUX_FD_H"]=" 1" +D["HAVE_LINUX_FS_H"]=" 1" +D["HAVE_INTTYPES_H"]=" 1" +D["HAVE_LINUX_HDREG_H"]=" 1" +D["HAVE_SYSLOG_H"]=" 1" +D["HAVE_PWD_H"]=" 1" +D["HAVE_MALLOC_H"]=" 1" +D["HAVE__BOOL"]=" 1" +D["HAVE_STDBOOL_H"]=" 1" +D["WORDS_LITTLEENDIAN"]=" 1" +D["HAVE_STRUCT_STAT_ST_BLOCKS"]=" 1" +D["HAVE_ST_BLOCKS"]=" 1" +D["HAVE_STRUCT_STAT_ST_RDEV"]=" 1" +D["HAVE_STRUCT_STAT_ST_ATIM"]=" 1" +D["HAVE_GETMNTENT"]=" 1" +D["HAVE_MBRTOWC"]=" 1" +D["LSTAT_FOLLOWS_SLASHED_SYMLINK"]=" 1" +D["HAVE_STRFTIME"]=" 1" +D["HAVE_UTIME_H"]=" 1" +D["HAVE_UTIME_NULL"]=" 1" +D["HAVE_VPRINTF"]=" 1" +D["HAVE_ATEXIT"]=" 1" +D["HAVE_BASENAME"]=" 1" +D["HAVE_DAEMON"]=" 1" +D["HAVE_DUP2"]=" 1" +D["HAVE_FDATASYNC"]=" 1" +D["HAVE_FFS"]=" 1" +D["HAVE_GETOPT_LONG"]=" 1" +D["HAVE_HASMNTOPT"]=" 1" +D["HAVE_MBSINIT"]=" 1" +D["HAVE_MEMMOVE"]=" 1" +D["HAVE_MEMSET"]=" 1" +D["HAVE_REALPATH"]=" 1" +D["HAVE_REGCOMP"]=" 1" +D["HAVE_SETLOCALE"]=" 1" +D["HAVE_SETXATTR"]=" 1" +D["HAVE_STRCASECMP"]=" 1" +D["HAVE_STRCHR"]=" 1" +D["HAVE_STRDUP"]=" 1" +D["HAVE_STRERROR"]=" 1" +D["HAVE_STRNLEN"]=" 1" +D["HAVE_STRSEP"]=" 1" +D["HAVE_STRTOL"]=" 1" +D["HAVE_STRTOUL"]=" 1" +D["HAVE_SYSCONF"]=" 1" +D["HAVE_UTIME"]=" 1" +D["HAVE_UTIMENSAT"]=" 1" +D["HAVE_GETTIMEOFDAY"]=" 1" +D["HAVE_CLOCK_GETTIME"]=" 1" +D["HAVE_FORK"]=" 1" +D["HAVE_MEMCPY"]=" 1" +D["HAVE_RANDOM"]=" 1" +D["HAVE_SNPRINTF"]=" 1" + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+[_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ][_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]*([\t (]|$)/ { + line = $ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} + ac_datarootdir_hack=' + s&@datadir@&${datarootdir}&g + s&@docdir@&${datarootdir}/doc/${PACKAGE_TARNAME}&g + s&@infodir@&${datarootdir}/info&g + s&@localedir@&${datarootdir}/locale&g + s&@mandir@&${datarootdir}/man&g + s&\${datarootdir}&${prefix}/share&g' ;; +esac +ac_sed_extra="/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +} + +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + "libtool":C) + + # 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 + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool 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. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# The names of the tagged configurations supported by this script. +available_tags="" + +# ### BEGIN LIBTOOL CONFIG + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The PATH separator for the build system. +PATH_SEPARATOR=$lt_PATH_SEPARATOR + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# convert \$build file names to \$host format. +to_host_file_cmd=$lt_cv_to_host_file_cmd + +# convert \$build files to toolchain format. +to_tool_file_cmd=$lt_cv_to_tool_file_cmd + +# An object symbol dumper. +OBJDUMP=$lt_OBJDUMP + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method = "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# How to find potential files when deplibs_check_method = "file_magic". +file_magic_glob=$lt_file_magic_glob + +# Find potential files using nocaseglob when deplibs_check_method = "file_magic". +want_nocaseglob=$lt_want_nocaseglob + +# DLL creation program. +DLLTOOL=$lt_DLLTOOL + +# Command to associate shared and link libraries. +sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd + +# The archiver. +AR=$lt_AR + +# Flags to create an archive. +AR_FLAGS=$lt_AR_FLAGS + +# How to feed a file listing to the archiver. +archiver_list_spec=$lt_archiver_list_spec + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# Specify filename containing input files for \$NM. +nm_file_list_spec=$lt_nm_file_list_spec + +# The root where to search for dependent libraries,and in which our libraries should be installed. +lt_sysroot=$lt_sysroot + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Manifest tool. +MANIFEST_TOOL=$lt_MANIFEST_TOOL + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain="$ac_aux_dir/ltmain.sh" + + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + if test x"$xsi_shell" = xyes; then + sed -e '/^func_dirname ()$/,/^} # func_dirname /c\ +func_dirname ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_basename ()$/,/^} # func_basename /c\ +func_basename ()\ +{\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\ +func_dirname_and_basename ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_stripname ()$/,/^} # func_stripname /c\ +func_stripname ()\ +{\ +\ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\ +\ # positional parameters, so assign one to ordinary parameter first.\ +\ func_stripname_result=${3}\ +\ func_stripname_result=${func_stripname_result#"${1}"}\ +\ func_stripname_result=${func_stripname_result%"${2}"}\ +} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\ +func_split_long_opt ()\ +{\ +\ func_split_long_opt_name=${1%%=*}\ +\ func_split_long_opt_arg=${1#*=}\ +} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\ +func_split_short_opt ()\ +{\ +\ func_split_short_opt_arg=${1#??}\ +\ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\ +} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\ +func_lo2o ()\ +{\ +\ case ${1} in\ +\ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\ +\ *) func_lo2o_result=${1} ;;\ +\ esac\ +} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_xform ()$/,/^} # func_xform /c\ +func_xform ()\ +{\ + func_xform_result=${1%.*}.lo\ +} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_arith ()$/,/^} # func_arith /c\ +func_arith ()\ +{\ + func_arith_result=$(( $* ))\ +} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_len ()$/,/^} # func_len /c\ +func_len ()\ +{\ + func_len_result=${#1}\ +} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + +fi + +if test x"$lt_shell_append" = xyes; then + sed -e '/^func_append ()$/,/^} # func_append /c\ +func_append ()\ +{\ + eval "${1}+=\\${2}"\ +} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\ +func_append_quoted ()\ +{\ +\ func_quote_for_eval "${2}"\ +\ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\ +} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + # Save a `func_append' function call where possible by direct use of '+=' + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +else + # Save a `func_append' function call even when '+=' is not available + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +fi + +if test x"$_lt_function_replace_fail" = x":"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5 +$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;} +fi + + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" + + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 diff --git a/config.sub b/config.sub new file mode 100755 index 0000000000000000000000000000000000000000..66c5074366061bb12d524ec56b75fd1caf9da336 --- /dev/null +++ b/config.sub @@ -0,0 +1,1798 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2014 Free Software Foundation, Inc. + +timestamp='2014-07-28' + +# This file 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 <http://www.gnu.org/licenses/>. +# +# 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. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2014 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | epiphany \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | or1k*-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-* | ppc64p7-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure b/configure new file mode 100755 index 0000000000000000000000000000000000000000..d41fa0feae437751d29dc5f489bd8a3abad151e7 --- /dev/null +++ b/configure @@ -0,0 +1,17629 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for ntfs-3g 2015.3.14. +# +# Report bugs to <ntfs-3g-devel@lists.sf.net>. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1 + + test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ + || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: ntfs-3g-devel@lists.sf.net about your system, including +$0: any error possibly output before this message. Then +$0: install a modern shell, or manually run the script +$0: under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + +SHELL=${CONFIG_SHELL-/bin/sh} + + +test -n "$DJDIR" || exec 7<&0 </dev/null +exec 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='ntfs-3g' +PACKAGE_TARNAME='ntfs-3g' +PACKAGE_VERSION='2015.3.14' +PACKAGE_STRING='ntfs-3g 2015.3.14' +PACKAGE_BUGREPORT='ntfs-3g-devel@lists.sf.net' +PACKAGE_URL='' + +ac_unique_file="src/ntfs-3g.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# ifdef HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_header_list= +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +ENABLE_QUARANTINED_FALSE +ENABLE_QUARANTINED_TRUE +ENABLE_EXTRAS_FALSE +ENABLE_EXTRAS_TRUE +ENABLE_NTFSPROGS_FALSE +ENABLE_NTFSPROGS_TRUE +ENABLE_NTFS_3G_FALSE +ENABLE_NTFS_3G_TRUE +ENABLE_MOUNT_HELPER_FALSE +ENABLE_MOUNT_HELPER_TRUE +INSTALL_LIBRARY_FALSE +INSTALL_LIBRARY_TRUE +REALLYSTATIC_FALSE +REALLYSTATIC_TRUE +RUN_LDCONFIG_FALSE +RUN_LDCONFIG_TRUE +NTFS_DEVICE_DEFAULT_IO_OPS_FALSE +NTFS_DEVICE_DEFAULT_IO_OPS_TRUE +WINDOWS_FALSE +WINDOWS_TRUE +GENERATE_LDSCRIPT_FALSE +GENERATE_LDSCRIPT_TRUE +FUSE_INTERNAL_FALSE +FUSE_INTERNAL_TRUE +OUTPUT_FORMAT +NTFSPROGS_STATIC_LIBS +LIBNTFS_LIBS +LIBNTFS_CPPFLAGS +MKNTFS_LIBS +MKNTFS_CPPFLAGS +LIBFUSE_LITE_LIBS +LIBFUSE_LITE_CFLAGS +LIBNTFS_3G_VERSION +rootlibdir +rootsbindir +rootbindir +ntfs3gincludedir +pkgconfigdir +LIBOBJS +all_libraries +all_includes +ENABLE_CRYPTO_FALSE +ENABLE_CRYPTO_TRUE +GNUTLS_LIBS +GNUTLS_CFLAGS +LIBGCRYPT_LIBS +LIBGCRYPT_CFLAGS +LIBGCRYPT_CONFIG +FUSE_MODULE_LIBS +FUSE_MODULE_CFLAGS +LDCONFIG +RM +MV +PKG_CONFIG_LIBDIR +PKG_CONFIG_PATH +PKG_CONFIG +OTOOL64 +OTOOL +LIPO +NMEDIT +DSYMUTIL +MANIFEST_TOOL +RANLIB +ac_ct_AR +AR +DLLTOOL +OBJDUMP +NM +ac_ct_DUMPBIN +DUMPBIN +LD +FGREP +SED +LIBTOOL +LN_S +EGREP +GREP +CPP +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +MAINT +MAINTAINER_MODE_FALSE +MAINTAINER_MODE_TRUE +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_os +target_vendor +target_cpu +target +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_silent_rules +enable_maintainer_mode +enable_debug +enable_warnings +enable_pedantic +enable_really_static +enable_mount_helper +enable_ldscript +enable_ldconfig +enable_library +enable_mtab +enable_posix_acls +enable_xattr_mappings +enable_device_default_io_ops +enable_ntfs_3g +enable_ntfsprogs +enable_crypto +enable_extras +enable_quarantined +enable_nfconv +enable_dependency_tracking +enable_shared +enable_static +with_pic +enable_fast_install +with_gnu_ld +with_sysroot +enable_libtool_lock +with_fuse +with_libgcrypt_prefix +with_uuid +with_hd +enable_largefile +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +PKG_CONFIG +PKG_CONFIG_PATH +PKG_CONFIG_LIBDIR +LDCONFIG +FUSE_MODULE_CFLAGS +FUSE_MODULE_LIBS +GNUTLS_CFLAGS +GNUTLS_LIBS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures ntfs-3g 2015.3.14 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/ntfs-3g] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] + --target=TARGET configure for building compilers for TARGET [HOST] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of ntfs-3g 2015.3.14:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer + --enable-debug enable debugging code and output + --enable-warnings enable lots of compiler warnings + --enable-pedantic enable compile pedantic mode + --enable-really-static create fully static binaries + --enable-mount-helper install mount helper [default=enabled for linux] + --enable-ldscript use ldscript instead of .so symlink + --disable-ldconfig do not update dynamic linker cache using ldconfig + --disable-library do not install libntfs-3g but link it into ntfs-3g + --disable-mtab disable and ignore usage of /etc/mtab + --enable-posix-acls enable POSIX ACL support + --enable-xattr-mappings enable system extended attributes mappings + --disable-device-default-io-ops + install default IO ops + --disable-ntfs-3g disable the ntfs-3g FUSE driver + --disable-ntfsprogs disable ntfsprogs utilities (default=no) + --enable-crypto enable crypto related code and utilities + (default=no) + --enable-extras enable extra ntfsprogs utilities (default=no) + --enable-quarantined enable quarantined ntfsprogs utilities (default=no) + --disable-nfconv disable the 'nfconv' patch, which adds support for + Unicode normalization form conversion when built on + Mac OS X [default=enabled for Mac OS X] + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --enable-shared[=PKGS] build shared libraries [default=yes] + --enable-static[=PKGS] build static libraries [default=yes] + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) + --disable-largefile omit support for large files + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use + both] + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + --with-sysroot=DIR Search for dependent libraries within DIR + (or the compiler's sysroot if not specified). + --with-fuse=<internal|external> + Select FUSE library: internal or external + [default=internal] + --with-libgcrypt-prefix=PFX + prefix where LIBGCRYPT is installed (optional) + + --with-uuid[=PFX] generate DCE compliant UUIDs, with optional prefix + to uuid library and headers [default=detect] + --without-uuid do not generate DCE compliant UUIDs + + --with-hd[=PFX] use Windows compliant disk geometry, with optional + prefix to hd library and headers [default=detect] + --without-hd do not use Windows compliant disk geometry + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + CPP C preprocessor + PKG_CONFIG path to pkg-config utility + PKG_CONFIG_PATH + directories to add to pkg-config's search path + PKG_CONFIG_LIBDIR + path overriding pkg-config's built-in search path + LDCONFIG ldconfig utility + FUSE_MODULE_CFLAGS + C compiler flags for FUSE_MODULE, overriding pkg-config + FUSE_MODULE_LIBS + linker flags for FUSE_MODULE, overriding pkg-config + GNUTLS_CFLAGS + C compiler flags for GNUTLS, overriding pkg-config + GNUTLS_LIBS linker flags for GNUTLS, overriding pkg-config + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to <ntfs-3g-devel@lists.sf.net>. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +ntfs-3g configure 2015.3.14 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## ----------------------------------------- ## +## Report this to ntfs-3g-devel@lists.sf.net ## +## ----------------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case <limits.h> declares $2. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES +# ---------------------------------------------------- +# Tries to find if the field MEMBER exists in type AGGR, after including +# INCLUDES, setting cache variable VAR accordingly. +ac_fn_c_check_member () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 +$as_echo_n "checking for $2.$3... " >&6; } +if eval \${$4+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (sizeof ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + eval "$4=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$4 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_member +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by ntfs-3g $as_me 2015.3.14, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +as_fn_append ac_header_list " utime.h" +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +LIBNTFS_3G_VERSION="86" + + +# Environment +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 +$as_echo_n "checking target system type... " >&6; } +if ${ac_cv_target+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$target_alias" = x; then + ac_cv_target=$ac_cv_host +else + ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 +$as_echo "$ac_cv_target" >&6; } +case $ac_cv_target in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; +esac +target=$ac_cv_target +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_target +shift +target_cpu=$1 +target_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +target_os=$* +IFS=$ac_save_IFS +case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac + + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +test -n "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +# Automake +am__api_version='1.14' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + + PACKAGE=${PACKAGE_NAME} + VERSION=${PACKAGE_VERSION} + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html> +# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html> +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542> + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: <http://www.gnu.org/software/coreutils/>. + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi +ac_config_headers="$ac_config_headers config.h" + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } + # Check whether --enable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then : + enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 +$as_echo "$USE_MAINTAINER_MODE" >&6; } + if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + + +# Options +# Check whether --enable-debug was given. +if test "${enable_debug+set}" = set; then : + enableval=$enable_debug; +else + enable_debug="no" + +fi + + +# Check whether --enable-warnings was given. +if test "${enable_warnings+set}" = set; then : + enableval=$enable_warnings; +else + enable_warnings="no" + +fi + + +# Check whether --enable-pedantic was given. +if test "${enable_pedantic+set}" = set; then : + enableval=$enable_pedantic; +else + enable_pedantic="no" + +fi + + +# Check whether --enable-really-static was given. +if test "${enable_really_static+set}" = set; then : + enableval=$enable_really_static; +else + enable_really_static="no" + +fi + + +# Check whether --enable-mount-helper was given. +if test "${enable_mount_helper+set}" = set; then : + enableval=$enable_mount_helper; +else + + case "${target_os}" in + linux*) enable_mount_helper="yes" ;; + *) enable_mount_helper="no" ;; + esac + + +fi + + +# Check whether --enable-ldscript was given. +if test "${enable_ldscript+set}" = set; then : + enableval=$enable_ldscript; +else + enable_ldscript="no" + +fi + + +# Check whether --enable-ldconfig was given. +if test "${enable_ldconfig+set}" = set; then : + enableval=$enable_ldconfig; +else + enable_ldconfig="yes" + +fi + + +# Check whether --enable-library was given. +if test "${enable_library+set}" = set; then : + enableval=$enable_library; +else + enable_library="yes" + +fi + + +# Check whether --enable-mtab was given. +if test "${enable_mtab+set}" = set; then : + enableval=$enable_mtab; +else + enable_mtab="yes" + +fi + + +# Check whether --enable-posix-acls was given. +if test "${enable_posix_acls+set}" = set; then : + enableval=$enable_posix_acls; +else + enable_posix_acls="no" + +fi + + +# Check whether --enable-xattr-mappings was given. +if test "${enable_xattr_mappings+set}" = set; then : + enableval=$enable_xattr_mappings; +else + enable_xattr_mappings="no" + +fi + + +# Check whether --enable-device-default-io-ops was given. +if test "${enable_device_default_io_ops+set}" = set; then : + enableval=$enable_device_default_io_ops; +else + enable_device_default_io_ops="yes" + +fi + + +# Check whether --enable-ntfs-3g was given. +if test "${enable_ntfs_3g+set}" = set; then : + enableval=$enable_ntfs_3g; +else + enable_ntfs_3g="yes" + +fi + + +# Check whether --enable-ntfsprogs was given. +if test "${enable_ntfsprogs+set}" = set; then : + enableval=$enable_ntfsprogs; +else + enable_ntfsprogs="yes" + +fi + + +# Check whether --enable-crypto was given. +if test "${enable_crypto+set}" = set; then : + enableval=$enable_crypto; +else + enable_crypto=no + +fi + + +# Check whether --enable-extras was given. +if test "${enable_extras+set}" = set; then : + enableval=$enable_extras; +else + enable_extras="no" + +fi + + +# Check whether --enable-quarantined was given. +if test "${enable_quarantined+set}" = set; then : + enableval=$enable_quarantined; +else + enable_quarantined="no" + +fi + + +# Check whether --enable-nfconv was given. +if test "${enable_nfconv+set}" = set; then : + enableval=$enable_nfconv; enable_nfconv="no" +else + + case "${target_os}" in + darwin*) enable_nfconv="yes" ;; + *) enable_nfconv="no" ;; + esac + + +fi + + +# pthread_rwlock_t requires _GNU_SOURCE +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdio.h> +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" +if test "x$ac_cv_header_minix_config_h" = xyes; then : + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h + + +$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h + + +$as_echo "#define _MINIX 1" >>confdefs.h + + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if ${ac_cv_safe_to_define___extensions__+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_safe_to_define___extensions__=yes +else + ac_cv_safe_to_define___extensions__=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h + + $as_echo "#define _ALL_SOURCE 1" >>confdefs.h + + $as_echo "#define _GNU_SOURCE 1" >>confdefs.h + + $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h + + $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h + + + + +# Programs +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + for ac_prog in gcc cc + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in gcc cc +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + + + +case `pwd` in + *\ * | *\ *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 +$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; +esac + + + +macro_version='2.4.2' +macro_revision='1.3337' + + + + + + + + + + + + + +ltmain="$ac_aux_dir/ltmain.sh" + +# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 +$as_echo_n "checking how to print strings... " >&6; } +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "" +} + +case "$ECHO" in + printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 +$as_echo "printf" >&6; } ;; + print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 +$as_echo "print -r" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 +$as_echo "cat" >&6; } ;; +esac + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if ${ac_cv_path_FGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_FGREP" || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + +test -z "$GREP" && GREP=grep + + + + + + + + + + + + + + + + + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in + *GNU* | *'with BFD'*) + test "$with_gnu_ld" != no && break + ;; + *) + test "$with_gnu_ld" != yes && break + ;; + esac + fi + done + IFS="$lt_save_ifs" +else + lt_cv_path_LD="$LD" # Let the user override the test with a path. +fi +fi + +LD="$lt_cv_path_LD" +if test -n "$LD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 </dev/null` in +*GNU* | *'with BFD'*) + lt_cv_prog_gnu_ld=yes + ;; +*) + lt_cv_prog_gnu_ld=no + ;; +esac +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 +$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } +if ${lt_cv_path_NM+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 +$as_echo "$lt_cv_path_NM" >&6; } +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + if test -n "$ac_tool_prefix"; then + for ac_prog in dumpbin "link -dump" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DUMPBIN"; then + ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DUMPBIN=$ac_cv_prog_DUMPBIN +if test -n "$DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 +$as_echo "$DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$DUMPBIN" && break + done +fi +if test -z "$DUMPBIN"; then + ac_ct_DUMPBIN=$DUMPBIN + for ac_prog in dumpbin "link -dump" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DUMPBIN"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN +if test -n "$ac_ct_DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 +$as_echo "$ac_ct_DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_DUMPBIN" && break +done + + if test "x$ac_ct_DUMPBIN" = x; then + DUMPBIN=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DUMPBIN=$ac_ct_DUMPBIN + fi +fi + + case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols" + ;; + *) + DUMPBIN=: + ;; + esac + fi + + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 +$as_echo_n "checking the name lister ($NM) interface... " >&6; } +if ${lt_cv_nm_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 +$as_echo "$lt_cv_nm_interface" >&6; } + +# find the maximum length of command line arguments +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 +$as_echo_n "checking the maximum length of command line arguments... " >&6; } +if ${lt_cv_sys_max_cmd_len+:} false; then : + $as_echo_n "(cached) " >&6 +else + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac + +fi + +if test -n $lt_cv_sys_max_cmd_len ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 +$as_echo "$lt_cv_sys_max_cmd_len" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 +$as_echo "none" >&6; } +fi +max_cmd_len=$lt_cv_sys_max_cmd_len + + + + + + +: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 +$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,b/c, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 +$as_echo "$xsi_shell" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 +$as_echo_n "checking whether the shell understands \"+=\"... " >&6; } +lt_shell_append=no +( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 +$as_echo "$lt_shell_append" >&6; } + + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi + + + + + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 +$as_echo_n "checking how to convert $build file names to $host format... " >&6; } +if ${lt_cv_to_host_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac + +fi + +to_host_file_cmd=$lt_cv_to_host_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 +$as_echo "$lt_cv_to_host_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 +$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } +if ${lt_cv_to_tool_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + #assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac + +fi + +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 +$as_echo "$lt_cv_to_tool_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 +$as_echo_n "checking for $LD option to reload object files... " >&6; } +if ${lt_cv_ld_reload_flag+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_reload_flag='-r' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 +$as_echo "$lt_cv_ld_reload_flag" >&6; } +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + if test "$GCC" != yes; then + reload_cmds=false + fi + ;; + darwin*) + if test "$GCC" = yes; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + +test -z "$OBJDUMP" && OBJDUMP=objdump + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 +$as_echo_n "checking how to recognize dependent libraries... " >&6; } +if ${lt_cv_deplibs_check_method+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# `unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# which responds to the $file_magic_cmd with a given extended regex. +# If you have `file' or equivalent on your system and you're not sure +# whether `pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[4-9]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[45]*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin. + if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[3-9]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 +$as_echo "$lt_cv_deplibs_check_method" >&6; } + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + + + + + + + + + + + + + + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. +set dummy ${ac_tool_prefix}dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DLLTOOL"; then + ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DLLTOOL=$ac_cv_prog_DLLTOOL +if test -n "$DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 +$as_echo "$DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DLLTOOL"; then + ac_ct_DLLTOOL=$DLLTOOL + # Extract the first word of "dlltool", so it can be a program name with args. +set dummy dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DLLTOOL"; then + ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DLLTOOL="dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL +if test -n "$ac_ct_DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 +$as_echo "$ac_ct_DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DLLTOOL" = x; then + DLLTOOL="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DLLTOOL=$ac_ct_DLLTOOL + fi +else + DLLTOOL="$ac_cv_prog_DLLTOOL" +fi + +test -z "$DLLTOOL" && DLLTOOL=dlltool + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 +$as_echo_n "checking how to associate runtime and link libraries... " >&6; } +if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh + # decide which to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd="$ECHO" + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 +$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + + + + + + + +if test -n "$ac_tool_prefix"; then + for ac_prog in ar + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} +: ${AR_FLAGS=cru} + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 +$as_echo_n "checking for archiver @FILE support... " >&6; } +if ${lt_cv_ar_at_file+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ar_at_file=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -ne 0; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 +$as_echo "$lt_cv_ar_at_file" >&6; } + +if test "x$lt_cv_ar_at_file" = xno; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +test -z "$STRIP" && STRIP=: + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +test -z "$RANLIB" && RANLIB=: + + + + + + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Check for command to grab the raw symbol name followed by C symbol from nm. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 +$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } +if ${lt_cv_sys_global_symbol_pipe+:} false; then : + $as_echo_n "(cached) " >&6 +else + +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[BCDEGRST]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[BCDT]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[ABCDGISTW]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[ABCDEGRST]' + fi + ;; +irix* | nonstopux*) + symcode='[BCDEGRST]' + ;; +osf*) + symcode='[BCDEGQRST]' + ;; +solaris*) + symcode='[BDRT]' + ;; +sco3.2v5*) + symcode='[DT]' + ;; +sysv4.2uw2*) + symcode='[DT]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[ABDT]' + ;; +sysv4) + symcode='[DFNSTU]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[ABCDGIRSTW]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK '"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Now try to grab the symbols. + nlist=conftest.nm + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 + (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done + +fi + +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 +$as_echo_n "checking for sysroot... " >&6; } + +# Check whether --with-sysroot was given. +if test "${with_sysroot+set}" = set; then : + withval=$with_sysroot; +else + with_sysroot=no +fi + + +lt_sysroot= +case ${with_sysroot} in #( + yes) + if test "$GCC" = yes; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5 +$as_echo "${with_sysroot}" >&6; } + as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 + ;; +esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 +$as_echo "${lt_sysroot:-no}" >&6; } + + + + + +# Check whether --enable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then : + enableval=$enable_libtool_lock; +fi + +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_i386" + ;; + powerpc64le-*linux*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*linux*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 +$as_echo_n "checking whether the C compiler needs -belf... " >&6; } +if ${lt_cv_cc_needs_belf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_cc_needs_belf=yes +else + lt_cv_cc_needs_belf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 +$as_echo "$lt_cv_cc_needs_belf" >&6; } + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD="${LD-ld}_sol2" + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks="$enable_libtool_lock" + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. +set dummy ${ac_tool_prefix}mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MANIFEST_TOOL"; then + ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL +if test -n "$MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 +$as_echo "$MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_MANIFEST_TOOL"; then + ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL + # Extract the first word of "mt", so it can be a program name with args. +set dummy mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_MANIFEST_TOOL"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL +if test -n "$ac_ct_MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 +$as_echo "$ac_ct_MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_MANIFEST_TOOL" = x; then + MANIFEST_TOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL + fi +else + MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" +fi + +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 +$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } +if ${lt_cv_path_mainfest_tool+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&5 + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 +$as_echo "$lt_cv_path_mainfest_tool" >&6; } +if test "x$lt_cv_path_mainfest_tool" != xyes; then + MANIFEST_TOOL=: +fi + + + + + + + case $host_os in + rhapsody* | darwin*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. +set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DSYMUTIL"; then + ac_ct_DSYMUTIL=$DSYMUTIL + # Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DSYMUTIL"; then + ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL +if test -n "$ac_ct_DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 +$as_echo "$ac_ct_DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DSYMUTIL" = x; then + DSYMUTIL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DSYMUTIL=$ac_ct_DSYMUTIL + fi +else + DSYMUTIL="$ac_cv_prog_DSYMUTIL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. +set dummy ${ac_tool_prefix}nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NMEDIT"; then + ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +NMEDIT=$ac_cv_prog_NMEDIT +if test -n "$NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 +$as_echo "$NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_NMEDIT"; then + ac_ct_NMEDIT=$NMEDIT + # Extract the first word of "nmedit", so it can be a program name with args. +set dummy nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_NMEDIT"; then + ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_NMEDIT="nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT +if test -n "$ac_ct_NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 +$as_echo "$ac_ct_NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_NMEDIT" = x; then + NMEDIT=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + NMEDIT=$ac_ct_NMEDIT + fi +else + NMEDIT="$ac_cv_prog_NMEDIT" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. +set dummy ${ac_tool_prefix}lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$LIPO"; then + ac_cv_prog_LIPO="$LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LIPO="${ac_tool_prefix}lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LIPO=$ac_cv_prog_LIPO +if test -n "$LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 +$as_echo "$LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LIPO"; then + ac_ct_LIPO=$LIPO + # Extract the first word of "lipo", so it can be a program name with args. +set dummy lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_LIPO"; then + ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LIPO="lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO +if test -n "$ac_ct_LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 +$as_echo "$ac_ct_LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_LIPO" = x; then + LIPO=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIPO=$ac_ct_LIPO + fi +else + LIPO="$ac_cv_prog_LIPO" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL"; then + ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL="${ac_tool_prefix}otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL=$ac_cv_prog_OTOOL +if test -n "$OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 +$as_echo "$OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL"; then + ac_ct_OTOOL=$OTOOL + # Extract the first word of "otool", so it can be a program name with args. +set dummy otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL"; then + ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL="otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL +if test -n "$ac_ct_OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 +$as_echo "$ac_ct_OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL" = x; then + OTOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL=$ac_ct_OTOOL + fi +else + OTOOL="$ac_cv_prog_OTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL64"; then + ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL64=$ac_cv_prog_OTOOL64 +if test -n "$OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 +$as_echo "$OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL64"; then + ac_ct_OTOOL64=$OTOOL64 + # Extract the first word of "otool64", so it can be a program name with args. +set dummy otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL64"; then + ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL64="otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 +if test -n "$ac_ct_OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 +$as_echo "$ac_ct_OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL64" = x; then + OTOOL64=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL64=$ac_ct_OTOOL64 + fi +else + OTOOL64="$ac_cv_prog_OTOOL64" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 +$as_echo_n "checking for -single_module linker flag... " >&6; } +if ${lt_cv_apple_cc_single_mod+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&5 + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test $_lt_result -eq 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&5 + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 +$as_echo "$lt_cv_apple_cc_single_mod" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 +$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } +if ${lt_cv_ld_exported_symbols_list+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_ld_exported_symbols_list=yes +else + lt_cv_ld_exported_symbols_list=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 +$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 +$as_echo_n "checking for -force_load linker flag... " >&6; } +if ${lt_cv_ld_force_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 + echo "$AR cru libconftest.a conftest.o" >&5 + $AR cru libconftest.a conftest.o 2>&5 + echo "$RANLIB libconftest.a" >&5 + $RANLIB libconftest.a 2>&5 + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&5 + elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&5 + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 +$as_echo "$lt_cv_ld_force_load" >&6; } + case $host_os in + rhapsody* | darwin1.[012]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[91]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[012]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac + +for ac_header in dlfcn.h +do : + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default +" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF + +fi + +done + + + + + +# Set options + + + + enable_dlopen=no + + + enable_win32_dll=no + + + # Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_shared=yes +fi + + + + + + + + + + # Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_static=yes +fi + + + + + + + + + + +# Check whether --with-pic was given. +if test "${with_pic+set}" = set; then : + withval=$with_pic; lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for lt_pkg in $withval; do + IFS="$lt_save_ifs" + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + pic_mode=default +fi + + +test -z "$pic_mode" && pic_mode=default + + + + + + + + # Check whether --enable-fast-install was given. +if test "${enable_fast_install+set}" = set; then : + enableval=$enable_fast_install; p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_fast_install=yes +fi + + + + + + + + + + + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +test -z "$LN_S" && LN_S="ln -s" + + + + + + + + + + + + + + +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 +$as_echo_n "checking for objdir... " >&6; } +if ${lt_cv_objdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 +$as_echo "$lt_cv_objdir" >&6; } +objdir=$lt_cv_objdir + + + + + +cat >>confdefs.h <<_ACEOF +#define LT_OBJDIR "$lt_cv_objdir/" +_ACEOF + + + + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld="$lt_cv_prog_gnu_ld" + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 +$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/${ac_tool_prefix}file; then + lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 +$as_echo_n "checking for file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/file; then + lt_cv_path_MAGIC_CMD="$ac_dir/file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + else + MAGIC_CMD=: + fi +fi + + fi + ;; +esac + +# Use C for the default configuration in the libtool script + +lt_save_CC="$CC" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +objext=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + +lt_prog_compiler_no_builtin_flag= + +if test "$GCC" = yes; then + case $cc_basename in + nvcc*) + lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; + *) + lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } +if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } + +if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : +fi + +fi + + + + + + + lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= + + + if test "$GCC" = yes; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + ;; + + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi + ;; + + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + lt_prog_compiler_wl='-Xlinker ' + if test -n "$lt_prog_compiler_pic"; then + lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='--shared' + lt_prog_compiler_static='--static' + ;; + nagfor*) + # NAG Fortran compiler + lt_prog_compiler_wl='-Wl,-Wl,,' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-qpic' + lt_prog_compiler_static='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='' + ;; + *Sun\ F* | *Sun*Fortran*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Wl,' + ;; + *Intel*\ [CF]*Compiler*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + *Portland\ Group*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + + rdos*) + lt_prog_compiler_static='-non_shared' + ;; + + solaris*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + lt_prog_compiler_wl='-Qoption ld ';; + *) + lt_prog_compiler_wl='-Wl,';; + esac + ;; + + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + unicos*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_can_build_shared=no + ;; + + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic=$lt_prog_compiler_pic +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 +$as_echo "$lt_cv_prog_compiler_pic" >&6; } +lt_prog_compiler_pic=$lt_cv_prog_compiler_pic + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } +if ${lt_cv_prog_compiler_pic_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works" = xyes; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi + +fi + + + + + + + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works=yes + fi + else + lt_cv_prog_compiler_static_works=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 +$as_echo "$lt_cv_prog_compiler_static_works" >&6; } + +if test x"$lt_cv_prog_compiler_static_works" = xyes; then + : +else + lt_prog_compiler_static= +fi + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + runpath_var= + allow_undefined_flag= + always_export_symbols=no + archive_cmds= + archive_expsym_cmds= + compiler_needs_object=no + enable_shared_with_static_runtimes=no + export_dynamic_flag_spec= + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + hardcode_automatic=no + hardcode_direct=no + hardcode_direct_absolute=no + hardcode_libdir_flag_spec= + hardcode_libdir_separator= + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + inherit_rpath=no + link_all_deplibs=unknown + module_cmds= + module_expsym_cmds= + old_archive_from_new_cmds= + old_archive_from_expsyms_cmds= + thread_safe_flag_spec= + whole_archive_flag_spec= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + esac + + ld_shlibs=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test "$with_gnu_ld" = yes; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; + *\ \(GNU\ Binutils\)\ [3-9]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test "$lt_use_gnu_ld_interface" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach <jrb3@best.com> says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + export_dynamic_flag_spec='${wl}--export-all-symbols' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs=no + fi + ;; + + haiku*) + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs=yes + ;; + + interix[3-9]*) + hardcode_direct=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + whole_archive_flag_spec= + tmp_sharedflag='--shared' ;; + xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + ld_shlibs=no + fi + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test "$ld_shlibs" = no; then + runpath_var= + hardcode_libdir_flag_spec= + export_dynamic_flag_spec= + whole_archive_flag_spec= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global + # defined symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds='' + hardcode_direct=yes + hardcode_direct_absolute=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + file_list_spec='${wl}-f,' + + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag=' ${wl}-bernotok' + allow_undefined_flag=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec='$convenience' + fi + archive_cmds_need_lc=yes + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + bsdi[45]*) + export_dynamic_flag_spec=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + always_export_symbols=yes + file_list_spec='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, )='true' + enable_shared_with_static_runtimes=yes + exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + old_postinstall_cmds='chmod 644 $oldlib' + postlink_cmds='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' + enable_shared_with_static_runtimes=yes + ;; + esac + ;; + + darwin* | rhapsody*) + + + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec='' + fi + link_all_deplibs=yes + allow_undefined_flag="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + + else + ld_shlibs=no + fi + + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + if test "$GCC" = yes; then + archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 +$as_echo_n "checking if $CC understands -b... " >&6; } +if ${lt_cv_prog_compiler__b+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler__b=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -b" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler__b=yes + fi + else + lt_cv_prog_compiler__b=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 +$as_echo "$lt_cv_prog_compiler__b" >&6; } + +if test x"$lt_cv_prog_compiler__b" = xyes; then + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' +else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' +fi + + ;; + esac + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + *) + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 +$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } +if ${lt_cv_irix_exported_symbol+:} false; then : + $as_echo_n "(cached) " >&6 +else + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo (void) { return 0; } +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_irix_exported_symbol=yes +else + lt_cv_irix_exported_symbol=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 +$as_echo "$lt_cv_irix_exported_symbol" >&6; } + if test "$lt_cv_irix_exported_symbol" = yes; then + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + fi + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + inherit_rpath=yes + link_all_deplibs=yes + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + hardcode_shlibpath_var=no + hardcode_direct_absolute=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + else + case $host_os in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + else + ld_shlibs=no + fi + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + archive_cmds_need_lc='no' + hardcode_libdir_separator=: + ;; + + solaris*) + no_undefined_flag=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' + fi + ;; + esac + link_all_deplibs=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds='$CC -r -o $output$reload_objs' + hardcode_direct=no + ;; + motorola) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag='${wl}-z,text' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag='${wl}-z,text' + allow_undefined_flag='${wl}-z,nodefs' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-R,$libdir' + hardcode_libdir_separator=':' + link_all_deplibs=yes + export_dynamic_flag_spec='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + export_dynamic_flag_spec='${wl}-Blargedynsym' + ;; + esac + fi + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 +$as_echo "$ld_shlibs" >&6; } +test "$ld_shlibs" = no && can_build_shared=no + +with_gnu_ld=$with_gnu_ld + + + + + + + + + + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + pic_flag=$lt_prog_compiler_pic + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc=no + else + lt_cv_archive_cmds_need_lc=yes + fi + allow_undefined_flag=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc" >&6; } + archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;; + *) lt_sed_strip_eq="s,=/,/,g" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[lt_foo]++; } + if (lt_freq[lt_foo] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's,/\([A-Za-z]:\),\1,g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib<name>.so + # instead of lib<name>.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Add ABI-specific directories to the system library path. + sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib" + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra" + + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || + test -n "$runpath_var" || + test "X$hardcode_automatic" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && + test "$hardcode_minus_L" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 +$as_echo "$hardcode_action" >&6; } + +if test "$hardcode_action" = relink || + test "$inherit_rpath" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + *) + ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" +if test "x$ac_cv_func_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 +$as_echo_n "checking for shl_load in -ldld... " >&6; } +if ${ac_cv_lib_dld_shl_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (); +int +main () +{ +return shl_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_shl_load=yes +else + ac_cv_lib_dld_shl_load=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 +$as_echo "$ac_cv_lib_dld_shl_load" >&6; } +if test "x$ac_cv_lib_dld_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" +else + ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" +if test "x$ac_cv_func_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 +$as_echo_n "checking for dlopen in -lsvld... " >&6; } +if ${ac_cv_lib_svld_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_svld_dlopen=yes +else + ac_cv_lib_svld_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 +$as_echo "$ac_cv_lib_svld_dlopen" >&6; } +if test "x$ac_cv_lib_svld_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 +$as_echo_n "checking for dld_link in -ldld... " >&6; } +if ${ac_cv_lib_dld_dld_link+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dld_link (); +int +main () +{ +return dld_link (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_dld_link=yes +else + ac_cv_lib_dld_dld_link=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 +$as_echo "$ac_cv_lib_dld_dld_link" >&6; } +if test "x$ac_cv_lib_dld_dld_link" = xyes; then : + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 +$as_echo_n "checking whether a program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#include <stdio.h> + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 +$as_echo "$lt_cv_dlopen_self" >&6; } + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 +$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self_static+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include <dlfcn.h> +#endif + +#include <stdio.h> + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 +$as_echo "$lt_cv_dlopen_self_static" >&6; } + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + + + + + + + + + + + + + + + + + +striplib= +old_striplib= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 +$as_echo_n "checking whether stripping libraries is possible... " >&6; } +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac +fi + + + + + + + + + + + + + # Report which library types will actually be built + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 +$as_echo_n "checking if libtool supports shared libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 +$as_echo "$can_build_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 +$as_echo_n "checking whether to build shared libraries... " >&6; } + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[4-9]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 +$as_echo "$enable_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 +$as_echo_n "checking whether to build static libraries... " >&6; } + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 +$as_echo "$enable_static" >&6; } + + + + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC="$lt_save_CC" + + + + + + + + + + + + + + + + ac_config_commands="$ac_config_commands libtool" + + + + +# Only expand once: + + + + + + + + + + + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.9.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 +$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + PKG_CONFIG="" + fi +fi + +# Extract the first word of "mv", so it can be a program name with args. +set dummy mv; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_MV+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MV in + [\\/]* | ?:[\\/]*) + ac_cv_path_MV="$MV" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_MV="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +MV=$ac_cv_path_MV +if test -n "$MV"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MV" >&5 +$as_echo "$MV" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +# Extract the first word of "rm", so it can be a program name with args. +set dummy rm; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_RM+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $RM in + [\\/]* | ?:[\\/]*) + ac_cv_path_RM="$RM" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_RM="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +RM=$ac_cv_path_RM +if test -n "$RM"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RM" >&5 +$as_echo "$RM" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +# Extract the first word of "sed", so it can be a program name with args. +set dummy sed; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $SED in + [\\/]* | ?:[\\/]*) + ac_cv_path_SED="$SED" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_SED="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +SED=$ac_cv_path_SED +if test -n "$SED"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SED" >&5 +$as_echo "$SED" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +# Extract the first word of "ldconfig", so it can be a program name with args. +set dummy ldconfig; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_LDCONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $LDCONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_LDCONFIG="$LDCONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /sbin /usr/sbin $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_LDCONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_LDCONFIG" && ac_cv_path_LDCONFIG="true" + ;; +esac +fi +LDCONFIG=$ac_cv_path_LDCONFIG +if test -n "$LDCONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LDCONFIG" >&5 +$as_echo "$LDCONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +# Environment +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking Windows OS" >&5 +$as_echo_n "checking Windows OS... " >&6; } +case "${target}" in +*-mingw32*|*-winnt*|*-cygwin*) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + WINDOWS="yes" + +$as_echo "#define WINDOWS 1" >>confdefs.h + + ;; +*) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + WINDOWS="no" + ;; +esac + +if test "x${enable_ntfs_3g}" = "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking fuse compatibility" >&5 +$as_echo_n "checking fuse compatibility... " >&6; } + case "${target_os}" in + linux*|solaris*) + +# Check whether --with-fuse was given. +if test "${with_fuse+set}" = set; then : + withval=$with_fuse; +else + with_fuse="internal" + +fi + + ;; + darwin*|netbsd*|kfreebsd*-gnu) + with_fuse="external" + ;; + freebsd*) + as_fn_error $? "Please see FreeBSD support at http://www.freshports.org/sysutils/fusefs-ntfs" "$LINENO" 5 + ;; + *) + as_fn_error $? "ntfs-3g can be built for Linux, FreeBSD, Mac OS X, NetBSD, and Solaris only." "$LINENO" 5 + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_fuse}" >&5 +$as_echo "${with_fuse}" >&6; } +else + with_fuse="none" +fi + +case "${target_os}" in +solaris*) + if test "x$GCC" != "xyes" ; then + as_fn_error $? "ntfs-3g can be built only with gcc on Solaris. Install it by 'pkg install gcc-dev' and retry.)" "$LINENO" 5 + fi + ;; +esac + +if test "${enable_ldscript}" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Output format" >&5 +$as_echo_n "checking Output format... " >&6; } + OUTPUT_FORMAT="$(${CC} ${CFLAGS} ${LDFLAGS} -Wl,--verbose 2>&1 | ${SED} -n 's/^OUTPUT_FORMAT("\([^"]*\)",.*/\1/p')" + if test -z "${OUTPUT_FORMAT}"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: None" >&5 +$as_echo "None" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${OUTPUT_FORMAT}" >&5 +$as_echo "${OUTPUT_FORMAT}" >&6; } + OUTPUT_FORMAT="OUTPUT_FORMAT ( ${OUTPUT_FORMAT} )" + fi +fi + +# Libraries +if test "${with_fuse}" = "internal"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 +$as_echo_n "checking for pthread_create in -lpthread... " >&6; } +if ${ac_cv_lib_pthread_pthread_create+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (); +int +main () +{ +return pthread_create (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pthread_pthread_create=yes +else + ac_cv_lib_pthread_pthread_create=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 +$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } +if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : + LIBFUSE_LITE_LIBS="${LIBFUSE_LITE_LIBS} -lpthread" +else + as_fn_error $? "Cannot find pthread library" "$LINENO" 5 + +fi + + +$as_echo "#define _REENTRANT 1" >>confdefs.h + + # required so that we re-compile anything + +$as_echo "#define FUSE_INTERNAL 1" >>confdefs.h + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Solaris OS" >&5 +$as_echo_n "checking Solaris OS... " >&6; } + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #if !((defined(sun) || defined(__sun)) && (defined(__SVR4) || defined(__svr4__))) + #error "Not a Solaris system." + #endif + + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + LIBFUSE_LITE_CFLAGS="${LIBFUSE_LITE_CFLAGS} -std=c99 -D__SOLARIS__ -D_XOPEN_SOURCE=600 -D__EXTENSIONS__" + LIBFUSE_LITE_LIBS="${LIBFUSE_LITE_LIBS} -lxnet" + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +elif test "${with_fuse}" = "external"; then + if test -z "$PKG_CONFIG"; then + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no" + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi + test "x${PKG_CONFIG}" = "xno" && as_fn_error $? "pkg-config wasn't found! Please install from your vendor, or see http://pkg-config.freedesktop.org/wiki/" "$LINENO" 5 + # Libraries often install their metadata .pc files in directories + # not searched by pkg-config. Let's workaround this. + export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/lib/pkgconfig:/usr/lib/pkgconfig:/opt/gnome/lib/pkgconfig:/usr/share/pkgconfig:/usr/local/lib/pkgconfig:$prefix/lib/pkgconfig:/opt/gnome/share/pkgconfig:/usr/local/share/pkgconfig + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for FUSE_MODULE" >&5 +$as_echo_n "checking for FUSE_MODULE... " >&6; } + +if test -n "$FUSE_MODULE_CFLAGS"; then + pkg_cv_FUSE_MODULE_CFLAGS="$FUSE_MODULE_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"fuse >= 2.6.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "fuse >= 2.6.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_FUSE_MODULE_CFLAGS=`$PKG_CONFIG --cflags "fuse >= 2.6.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$FUSE_MODULE_LIBS"; then + pkg_cv_FUSE_MODULE_LIBS="$FUSE_MODULE_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"fuse >= 2.6.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "fuse >= 2.6.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_FUSE_MODULE_LIBS=`$PKG_CONFIG --libs "fuse >= 2.6.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + FUSE_MODULE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "fuse >= 2.6.0" 2>&1` + else + FUSE_MODULE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "fuse >= 2.6.0" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$FUSE_MODULE_PKG_ERRORS" >&5 + + + as_fn_error $? "FUSE >= 2.6.0 was not found. Either older FUSE is still present, or FUSE is not fully installed (e.g. fuse, libfuse, libfuse2, libfuse-dev, etc packages). Source code: http://fuse.sf.net" "$LINENO" 5 + + +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + + as_fn_error $? "FUSE >= 2.6.0 was not found. Either older FUSE is still present, or FUSE is not fully installed (e.g. fuse, libfuse, libfuse2, libfuse-dev, etc packages). Source code: http://fuse.sf.net" "$LINENO" 5 + + +else + FUSE_MODULE_CFLAGS=$pkg_cv_FUSE_MODULE_CFLAGS + FUSE_MODULE_LIBS=$pkg_cv_FUSE_MODULE_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +fi + FUSE_LIB_PATH=`$PKG_CONFIG --libs-only-L fuse | sed -e 's,//*,/,g' -e 's, *$,,'` +fi + +# Autodetect whether we can build crypto stuff or not. +compile_crypto=false +if test "$enable_crypto" != "no"; then + have_libgcrypt=false + + +# Check whether --with-libgcrypt-prefix was given. +if test "${with_libgcrypt_prefix+set}" = set; then : + withval=$with_libgcrypt_prefix; libgcrypt_config_prefix="$withval" +else + libgcrypt_config_prefix="" +fi + + if test x$libgcrypt_config_prefix != x ; then + if test x${LIBGCRYPT_CONFIG+set} != xset ; then + LIBGCRYPT_CONFIG=$libgcrypt_config_prefix/bin/libgcrypt-config + fi + fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}libgcrypt-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}libgcrypt-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_LIBGCRYPT_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $LIBGCRYPT_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_LIBGCRYPT_CONFIG="$LIBGCRYPT_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_LIBGCRYPT_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +LIBGCRYPT_CONFIG=$ac_cv_path_LIBGCRYPT_CONFIG +if test -n "$LIBGCRYPT_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBGCRYPT_CONFIG" >&5 +$as_echo "$LIBGCRYPT_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_LIBGCRYPT_CONFIG"; then + ac_pt_LIBGCRYPT_CONFIG=$LIBGCRYPT_CONFIG + # Extract the first word of "libgcrypt-config", so it can be a program name with args. +set dummy libgcrypt-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_LIBGCRYPT_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_LIBGCRYPT_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_LIBGCRYPT_CONFIG="$ac_pt_LIBGCRYPT_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_LIBGCRYPT_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_LIBGCRYPT_CONFIG=$ac_cv_path_ac_pt_LIBGCRYPT_CONFIG +if test -n "$ac_pt_LIBGCRYPT_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_LIBGCRYPT_CONFIG" >&5 +$as_echo "$ac_pt_LIBGCRYPT_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_LIBGCRYPT_CONFIG" = x; then + LIBGCRYPT_CONFIG="no" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIBGCRYPT_CONFIG=$ac_pt_LIBGCRYPT_CONFIG + fi +else + LIBGCRYPT_CONFIG="$ac_cv_path_LIBGCRYPT_CONFIG" +fi + + tmp=1.2.2 + if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then + req_libgcrypt_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` + min_libgcrypt_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` + else + req_libgcrypt_api=0 + min_libgcrypt_version="$tmp" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBGCRYPT - version >= $min_libgcrypt_version" >&5 +$as_echo_n "checking for LIBGCRYPT - version >= $min_libgcrypt_version... " >&6; } + ok=no + if test "$LIBGCRYPT_CONFIG" != "no" ; then + req_major=`echo $min_libgcrypt_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` + req_minor=`echo $min_libgcrypt_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` + req_micro=`echo $min_libgcrypt_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` + libgcrypt_config_version=`$LIBGCRYPT_CONFIG --version` + major=`echo $libgcrypt_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1/'` + minor=`echo $libgcrypt_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\2/'` + micro=`echo $libgcrypt_config_version | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\3/'` + if test "$major" -gt "$req_major"; then + ok=yes + else + if test "$major" -eq "$req_major"; then + if test "$minor" -gt "$req_minor"; then + ok=yes + else + if test "$minor" -eq "$req_minor"; then + if test "$micro" -ge "$req_micro"; then + ok=yes + fi + fi + fi + fi + fi + fi + if test $ok = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes ($libgcrypt_config_version)" >&5 +$as_echo "yes ($libgcrypt_config_version)" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + if test $ok = yes; then + # If we have a recent libgcrypt, we should also check that the + # API is compatible + if test "$req_libgcrypt_api" -gt 0 ; then + tmp=`$LIBGCRYPT_CONFIG --api-version 2>/dev/null || echo 0` + if test "$tmp" -gt 0 ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking LIBGCRYPT API version" >&5 +$as_echo_n "checking LIBGCRYPT API version... " >&6; } + if test "$req_libgcrypt_api" -eq "$tmp" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: okay" >&5 +$as_echo "okay" >&6; } + else + ok=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: does not match. want=$req_libgcrypt_api got=$tmp" >&5 +$as_echo "does not match. want=$req_libgcrypt_api got=$tmp" >&6; } + fi + fi + fi + fi + if test $ok = yes; then + LIBGCRYPT_CFLAGS=`$LIBGCRYPT_CONFIG --cflags` + LIBGCRYPT_LIBS=`$LIBGCRYPT_CONFIG --libs` + have_libgcrypt=true + libgcrypt_config_host=`$LIBGCRYPT_CONFIG --host 2>/dev/null || echo none` + if test x"$libgcrypt_config_host" != xnone ; then + if test x"$libgcrypt_config_host" != x"$host" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +*** +*** The config script $LIBGCRYPT_CONFIG was +*** built for $libgcrypt_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-libgcrypt-prefix +*** to specify a matching config script. +***" >&5 +$as_echo "$as_me: WARNING: +*** +*** The config script $LIBGCRYPT_CONFIG was +*** built for $libgcrypt_config_host and thus may not match the +*** used host $host. +*** You may want to use the configure option --with-libgcrypt-prefix +*** to specify a matching config script. +***" >&2;} + fi + fi + else + LIBGCRYPT_CFLAGS="" + LIBGCRYPT_LIBS="" + + if test "$enable_crypto" = "yes"; then + as_fn_error $? "ntfsprogs crypto code requires the gcrypt library." "$LINENO" 5 + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ntfsprogs crypto code requires the gcrypt library." >&5 +$as_echo "$as_me: WARNING: ntfsprogs crypto code requires the gcrypt library." >&2;} + fi + + fi + + + + have_libgnutls=false + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNUTLS" >&5 +$as_echo_n "checking for GNUTLS... " >&6; } + +if test -n "$GNUTLS_CFLAGS"; then + pkg_cv_GNUTLS_CFLAGS="$GNUTLS_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gnutls >= 1.4.4\""; } >&5 + ($PKG_CONFIG --exists --print-errors "gnutls >= 1.4.4") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_GNUTLS_CFLAGS=`$PKG_CONFIG --cflags "gnutls >= 1.4.4" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$GNUTLS_LIBS"; then + pkg_cv_GNUTLS_LIBS="$GNUTLS_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gnutls >= 1.4.4\""; } >&5 + ($PKG_CONFIG --exists --print-errors "gnutls >= 1.4.4") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_GNUTLS_LIBS=`$PKG_CONFIG --libs "gnutls >= 1.4.4" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + GNUTLS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gnutls >= 1.4.4" 2>&1` + else + GNUTLS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gnutls >= 1.4.4" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$GNUTLS_PKG_ERRORS" >&5 + + if test "$enable_crypto" = "yes"; then + as_fn_error $? "ntfsprogs crypto code requires the gnutls library." "$LINENO" 5 + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ntfsprogs crypto code requires the gnutls library." >&5 +$as_echo "$as_me: WARNING: ntfsprogs crypto code requires the gnutls library." >&2;} + fi + +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + if test "$enable_crypto" = "yes"; then + as_fn_error $? "ntfsprogs crypto code requires the gnutls library." "$LINENO" 5 + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ntfsprogs crypto code requires the gnutls library." >&5 +$as_echo "$as_me: WARNING: ntfsprogs crypto code requires the gnutls library." >&2;} + fi + +else + GNUTLS_CFLAGS=$pkg_cv_GNUTLS_CFLAGS + GNUTLS_LIBS=$pkg_cv_GNUTLS_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_libgnutls=true +fi + if test "$have_libgcrypt" = "true"; then + if test "$have_libgnutls" = "true"; then + compile_crypto=true + +$as_echo "#define ENABLE_CRYPTO 1" >>confdefs.h + + fi + fi +fi + if $compile_crypto; then + ENABLE_CRYPTO_TRUE= + ENABLE_CRYPTO_FALSE='#' +else + ENABLE_CRYPTO_TRUE='#' + ENABLE_CRYPTO_FALSE= +fi + + +# add --with-extra-includes and --with-extra-libs switch to ./configure +all_libraries="$all_libraries $USER_LDFLAGS" +all_includes="$all_includes $USER_INCLUDES" + + + +# Specify support for generating DCE compliant UUIDs (aka GUIDs). We check if +# uuid/uuid.h header is present and the uuid library is present that goes with +# it and then check if uuid_generate() is present and usable. +# +# DCE UUIDs are enabled by default and can be disabled with the --disable-uuid +# option to the configure script. + +# Check whether --with-uuid was given. +if test "${with_uuid+set}" = set; then : + withval=$with_uuid; if test "$with_uuid" = "yes"; then + extrapath=default + elif test "$with_uuid" = "no"; then + extrapath= + else + extrapath=$with_uuid + fi +else + extrapath=default + +fi + +if test "x$extrapath" != "x"; then + if test "x$extrapath" != "xdefault"; then + MKNTFS_CPPFLAGS="$MKNTFS_CPPFLAGS -I$extrapath/include" + MKNTFS_LIBS="$MKNTFS_LIBS -L$extrapath/lib" + fi + + search_for_luuid="yes" + ac_fn_c_check_header_mongrel "$LINENO" "uuid/uuid.h" "ac_cv_header_uuid_uuid_h" "$ac_includes_default" +if test "x$ac_cv_header_uuid_uuid_h" = xyes; then : + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ntfsprogs DCE compliant UUID generation code requires the uuid library." >&5 +$as_echo "$as_me: WARNING: ntfsprogs DCE compliant UUID generation code requires the uuid library." >&2;} + search_for_luuid="no" + +fi + + + + if test "x$search_for_luuid" != "xno"; then + # Look for uuid_generate in the standard C library. + ac_fn_c_check_func "$LINENO" "uuid_generate" "ac_cv_func_uuid_generate" +if test "x$ac_cv_func_uuid_generate" = xyes; then : + + +$as_echo "#define ENABLE_UUID 1" >>confdefs.h + + search_for_luuid="no" + +fi + + fi + + if test "x$search_for_luuid" != "xno"; then + # Look for uuid_generate in the 'uuid' library. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uuid_generate in -luuid" >&5 +$as_echo_n "checking for uuid_generate in -luuid... " >&6; } +if ${ac_cv_lib_uuid_uuid_generate+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-luuid $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char uuid_generate (); +int +main () +{ +return uuid_generate (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_uuid_uuid_generate=yes +else + ac_cv_lib_uuid_uuid_generate=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_generate" >&5 +$as_echo "$ac_cv_lib_uuid_uuid_generate" >&6; } +if test "x$ac_cv_lib_uuid_uuid_generate" = xyes; then : + + +$as_echo "#define ENABLE_UUID 1" >>confdefs.h + + MKNTFS_LIBS="$MKNTFS_LIBS -luuid" + search_for_luuid="no" + +fi + + fi + + if test "x$search_for_luuid" != "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ntfsprogs DCE compliant UUID generation code requires the uuid library." >&5 +$as_echo "$as_me: WARNING: ntfsprogs DCE compliant UUID generation code requires the uuid library." >&2;} + fi +fi + +# Specify support for obtaining the correct BIOS legacy geometry needed for +# Windows to boot in CHS mode. We check if hd.h header is present and the hd +# library is present that goes with it and then check if the hd_list() function +# is present and usable. +# +# Using the hd library is enabled by default and can be disabled with the +# --disable-hd option to the configure script. + +# Check whether --with-hd was given. +if test "${with_hd+set}" = set; then : + withval=$with_hd; if test "$with_hd" = "yes"; then + extrapath2=default + elif test "$with_hd" = "no"; then + extrapath2= + else + extrapath2=$with_hd + fi +else + extrapath2=default + +fi + +if test "x$extrapath2" != "x"; then + if test "x$extrapath2" != "xdefault"; then + LIBNTFS_CPPFLAGS="$LIBNTFS_CPPFLAGS -I$extrapath2/include" + LIBNTFS_LIBS="$LIBNTFS_LIBS -L$extrapath2/lib" + fi + ac_fn_c_check_header_mongrel "$LINENO" "hd.h" "ac_cv_header_hd_h" "$ac_includes_default" +if test "x$ac_cv_header_hd_h" = xyes; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for hd_list in -lhd" >&5 +$as_echo_n "checking for hd_list in -lhd... " >&6; } +if ${ac_cv_lib_hd_hd_list+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lhd $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char hd_list (); +int +main () +{ +return hd_list (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_hd_hd_list=yes +else + ac_cv_lib_hd_hd_list=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_hd_hd_list" >&5 +$as_echo "$ac_cv_lib_hd_hd_list" >&6; } +if test "x$ac_cv_lib_hd_hd_list" = xyes; then : + +$as_echo "#define ENABLE_HD 1" >>confdefs.h + + LIBNTFS_LIBS="$LIBNTFS_LIBS -lhd" + NTFSPROGS_STATIC_LIBS="$NTFSPROGS_STATIC_LIBS -lhd" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ntfsprogs Windows compliant geometry code requires the hd library." >&5 +$as_echo "$as_me: WARNING: ntfsprogs Windows compliant geometry code requires the hd library." >&2;} +fi + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ntfsprogs Windows compliant geometry code requires the hd library." >&5 +$as_echo "$as_me: WARNING: ntfsprogs Windows compliant geometry code requires the hd library." >&2;} +fi + + +fi + +# Checks for header files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +for ac_header in ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \ + mntent.h stddef.h stdint.h stdlib.h stdio.h stdarg.h string.h \ + strings.h errno.h time.h unistd.h utime.h wchar.h getopt.h features.h \ + regex.h endian.h byteswap.h sys/byteorder.h sys/disk.h sys/endian.h \ + sys/param.h sys/ioctl.h sys/mkdev.h sys/mount.h sys/stat.h sys/types.h \ + sys/vfs.h sys/statvfs.h sys/sysmacros.h linux/major.h linux/fd.h \ + linux/fs.h inttypes.h linux/hdreg.h \ + machine/endian.h windows.h syslog.h pwd.h malloc.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Checks for typedefs, structures, and compiler characteristics. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5 +$as_echo_n "checking for stdbool.h that conforms to C99... " >&6; } +if ${ac_cv_header_stdbool_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include <stdbool.h> + #ifndef bool + "error: bool is not defined" + #endif + #ifndef false + "error: false is not defined" + #endif + #if false + "error: false is not 0" + #endif + #ifndef true + "error: true is not defined" + #endif + #if true != 1 + "error: true is not 1" + #endif + #ifndef __bool_true_false_are_defined + "error: __bool_true_false_are_defined is not defined" + #endif + + struct s { _Bool s: 1; _Bool t; } s; + + char a[true == 1 ? 1 : -1]; + char b[false == 0 ? 1 : -1]; + char c[__bool_true_false_are_defined == 1 ? 1 : -1]; + char d[(bool) 0.5 == true ? 1 : -1]; + /* See body of main program for 'e'. */ + char f[(_Bool) 0.0 == false ? 1 : -1]; + char g[true]; + char h[sizeof (_Bool)]; + char i[sizeof s.t]; + enum { j = false, k = true, l = false * true, m = true * 256 }; + /* The following fails for + HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ + _Bool n[m]; + char o[sizeof n == m * sizeof n[0] ? 1 : -1]; + char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; + /* Catch a bug in an HP-UX C compiler. See + http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html + */ + _Bool q = true; + _Bool *pq = &q; + +int +main () +{ + + bool e = &s; + *pq |= q; + *pq |= ! q; + /* Refer to every declared value, to avoid compiler optimizations. */ + return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l + + !m + !n + !o + !p + !q + !pq); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdbool_h=yes +else + ac_cv_header_stdbool_h=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5 +$as_echo "$ac_cv_header_stdbool_h" >&6; } + ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" +if test "x$ac_cv_type__Bool" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE__BOOL 1 +_ACEOF + + +fi + + +if test $ac_cv_header_stdbool_h = yes; then + +$as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 +$as_echo_n "checking whether byte ordering is bigendian... " >&6; } +if ${ac_cv_c_bigendian+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_bigendian=unknown + # See if we're dealing with a universal compiler. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __APPLE_CC__ + not a universal capable compiler + #endif + typedef int dummy; + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # Check for potential -arch flags. It is not universal unless + # there are at least two -arch flags with different values. + ac_arch= + ac_prev= + for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do + if test -n "$ac_prev"; then + case $ac_word in + i?86 | x86_64 | ppc | ppc64) + if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then + ac_arch=$ac_word + else + ac_cv_c_bigendian=universal + break + fi + ;; + esac + ac_prev= + elif test "x$ac_word" = "x-arch"; then + ac_prev=arch + fi + done +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $ac_cv_c_bigendian = unknown; then + # See if sys/param.h defines the BYTE_ORDER macro. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> + #include <sys/param.h> + +int +main () +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ + && LITTLE_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> + #include <sys/param.h> + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <limits.h> + +int +main () +{ +#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to _BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <limits.h> + +int +main () +{ +#ifndef _BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # Compile a test program. + if test "$cross_compiling" = yes; then : + # Try to guess by grepping values from an object file. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +short int ascii_mm[] = + { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; + short int ascii_ii[] = + { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; + int use_ascii (int i) { + return ascii_mm[i] + ascii_ii[i]; + } + short int ebcdic_ii[] = + { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; + short int ebcdic_mm[] = + { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; + int use_ebcdic (int i) { + return ebcdic_mm[i] + ebcdic_ii[i]; + } + extern int foo; + +int +main () +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + ac_cv_c_bigendian=yes + fi + if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi + fi +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_bigendian=no +else + ac_cv_c_bigendian=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 +$as_echo "$ac_cv_c_bigendian" >&6; } + case $ac_cv_c_bigendian in #( + yes) + $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h +;; #( + no) + + +$as_echo "#define WORDS_LITTLEENDIAN 1" >>confdefs.h + + + ;; #( + universal) + +$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h + + ;; #( + *) + as_fn_error $? "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if ${ac_cv_c_const+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +#ifndef __cplusplus + /* Ultrix mips cc rejects this sort of thing. */ + typedef int charset[2]; + const charset cs = { 0, 0 }; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this sort of thing. */ + char tx; + char *t = &tx; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; } bx; + struct s *b = &bx; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_const=yes +else + ac_cv_c_const=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +$as_echo "#define const /**/" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" +if test "x$ac_cv_type_off_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define off_t long int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +ac_fn_c_check_member "$LINENO" "struct stat" "st_blocks" "ac_cv_member_struct_stat_st_blocks" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_blocks" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_BLOCKS 1 +_ACEOF + + +$as_echo "#define HAVE_ST_BLOCKS 1" >>confdefs.h + +else + case " $LIBOBJS " in + *" fileblocks.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS fileblocks.$ac_objext" + ;; +esac + +fi + + +ac_fn_c_check_member "$LINENO" "struct stat" "st_rdev" "ac_cv_member_struct_stat_st_rdev" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_rdev" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_RDEV 1 +_ACEOF + + +fi + +ac_fn_c_check_member "$LINENO" "struct stat" "st_atim" "ac_cv_member_struct_stat_st_atim" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_atim" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_ATIM 1 +_ACEOF + + +fi + +ac_fn_c_check_member "$LINENO" "struct stat" "st_atimespec" "ac_cv_member_struct_stat_st_atimespec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_atimespec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_ATIMESPEC 1 +_ACEOF + + +fi + +ac_fn_c_check_member "$LINENO" "struct stat" "st_atimensec" "ac_cv_member_struct_stat_st_atimensec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_atimensec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_ATIMENSEC 1 +_ACEOF + + +fi + + +# For the 'nfconv' patch (Mac OS X only): +case "${target_os}" in +darwin*) + if test "${enable_nfconv}" = "yes"; then + ac_fn_c_check_header_mongrel "$LINENO" "CoreFoundation/CoreFoundation.h" "ac_cv_header_CoreFoundation_CoreFoundation_h" "$ac_includes_default" +if test "x$ac_cv_header_CoreFoundation_CoreFoundation_h" = xyes; then : + + LDFLAGS="${LDFLAGS} -framework CoreFoundation" + +$as_echo "#define ENABLE_NFCONV 1" >>confdefs.h + + +else + as_fn_error $? "Cannot find CoreFoundation required for 'nfconv' functionality Mac OS X. You may use the --disable-nfconv 'configure' option to avoid this error." "$LINENO" 5 + +fi + + + fi + ;; +esac + +# Checks for library functions. +# getmntent is in the standard C library on UNICOS, in -lsun on Irix 4, +# -lseq on Dynix/PTX, -lgen on Unixware. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing getmntent" >&5 +$as_echo_n "checking for library containing getmntent... " >&6; } +if ${ac_cv_search_getmntent+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char getmntent (); +int +main () +{ +return getmntent (); + ; + return 0; +} +_ACEOF +for ac_lib in '' sun seq gen; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_getmntent=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_getmntent+:} false; then : + break +fi +done +if ${ac_cv_search_getmntent+:} false; then : + +else + ac_cv_search_getmntent=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_getmntent" >&5 +$as_echo "$ac_cv_search_getmntent" >&6; } +ac_res=$ac_cv_search_getmntent +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + ac_cv_func_getmntent=yes + +$as_echo "#define HAVE_GETMNTENT 1" >>confdefs.h + +else + ac_cv_func_getmntent=no +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether mbrtowc and mbstate_t are properly declared" >&5 +$as_echo_n "checking whether mbrtowc and mbstate_t are properly declared... " >&6; } +if ${ac_cv_func_mbrtowc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <wchar.h> +int +main () +{ +wchar_t wc; + char const s[] = ""; + size_t n = 1; + mbstate_t state; + return ! (sizeof state && (mbrtowc) (&wc, s, n, &state)); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_func_mbrtowc=yes +else + ac_cv_func_mbrtowc=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_mbrtowc" >&5 +$as_echo "$ac_cv_func_mbrtowc" >&6; } + if test $ac_cv_func_mbrtowc = yes; then + +$as_echo "#define HAVE_MBRTOWC 1" >>confdefs.h + + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working memcmp" >&5 +$as_echo_n "checking for working memcmp... " >&6; } +if ${ac_cv_func_memcmp_working+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_memcmp_working=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = '\100', c1 = '\200', c2 = '\201'; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + return 1; + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + return 1; + } + return 0; + } + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_memcmp_working=yes +else + ac_cv_func_memcmp_working=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_memcmp_working" >&5 +$as_echo "$ac_cv_func_memcmp_working" >&6; } +test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in + *" memcmp.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" + ;; +esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether lstat correctly handles trailing slash" >&5 +$as_echo_n "checking whether lstat correctly handles trailing slash... " >&6; } +if ${ac_cv_func_lstat_dereferences_slashed_symlink+:} false; then : + $as_echo_n "(cached) " >&6 +else + rm -f conftest.sym conftest.file +echo >conftest.file +if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then + if test "$cross_compiling" = yes; then : + ac_cv_func_lstat_dereferences_slashed_symlink=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + /* Linux will dereference the symlink and fail, as required by POSIX. + That is better in the sense that it means we will not + have to compile and use the lstat wrapper. */ + return lstat ("conftest.sym/", &sbuf) == 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_lstat_dereferences_slashed_symlink=yes +else + ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +else + # If the `ln -s' command failed, then we probably don't even + # have an lstat function. + ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f conftest.sym conftest.file + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 +$as_echo "$ac_cv_func_lstat_dereferences_slashed_symlink" >&6; } + +test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && + +cat >>confdefs.h <<_ACEOF +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 +_ACEOF + + +if test "x$ac_cv_func_lstat_dereferences_slashed_symlink" = xno; then + case " $LIBOBJS " in + *" lstat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS lstat.$ac_objext" + ;; +esac + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat accepts an empty string" >&5 +$as_echo_n "checking whether stat accepts an empty string... " >&6; } +if ${ac_cv_func_stat_empty_string_bug+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_stat_empty_string_bug=yes +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + return stat ("", &sbuf) == 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_stat_empty_string_bug=no +else + ac_cv_func_stat_empty_string_bug=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_stat_empty_string_bug" >&5 +$as_echo "$ac_cv_func_stat_empty_string_bug" >&6; } +if test $ac_cv_func_stat_empty_string_bug = yes; then + case " $LIBOBJS " in + *" stat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS stat.$ac_objext" + ;; +esac + + +cat >>confdefs.h <<_ACEOF +#define HAVE_STAT_EMPTY_STRING_BUG 1 +_ACEOF + +fi + +for ac_func in strftime +do : + ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime" +if test "x$ac_cv_func_strftime" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STRFTIME 1 +_ACEOF + +else + # strftime is in -lintl on SCO UNIX. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for strftime in -lintl" >&5 +$as_echo_n "checking for strftime in -lintl... " >&6; } +if ${ac_cv_lib_intl_strftime+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lintl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char strftime (); +int +main () +{ +return strftime (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_intl_strftime=yes +else + ac_cv_lib_intl_strftime=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_intl_strftime" >&5 +$as_echo "$ac_cv_lib_intl_strftime" >&6; } +if test "x$ac_cv_lib_intl_strftime" = xyes; then : + $as_echo "#define HAVE_STRFTIME 1" >>confdefs.h + +LIBS="-lintl $LIBS" +fi + +fi +done + + + + + for ac_header in $ac_header_list +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether utime accepts a null argument" >&5 +$as_echo_n "checking whether utime accepts a null argument... " >&6; } +if ${ac_cv_func_utime_null+:} false; then : + $as_echo_n "(cached) " >&6 +else + rm -f conftest.data; >conftest.data +# Sequent interprets utime(file, 0) to mean use start of epoch. Wrong. +if test "$cross_compiling" = yes; then : + ac_cv_func_utime_null='guessing yes' +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + #ifdef HAVE_UTIME_H + # include <utime.h> + #endif +int +main () +{ +struct stat s, t; + return ! (stat ("conftest.data", &s) == 0 + && utime ("conftest.data", 0) == 0 + && stat ("conftest.data", &t) == 0 + && t.st_mtime >= s.st_mtime + && t.st_mtime - s.st_mtime < 120); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_utime_null=yes +else + ac_cv_func_utime_null=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_utime_null" >&5 +$as_echo "$ac_cv_func_utime_null" >&6; } +if test "x$ac_cv_func_utime_null" != xno; then + ac_cv_func_utime_null=yes + +$as_echo "#define HAVE_UTIME_NULL 1" >>confdefs.h + +fi +rm -f conftest.data + +for ac_func in vprintf +do : + ac_fn_c_check_func "$LINENO" "vprintf" "ac_cv_func_vprintf" +if test "x$ac_cv_func_vprintf" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_VPRINTF 1 +_ACEOF + +ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt" +if test "x$ac_cv_func__doprnt" = xyes; then : + +$as_echo "#define HAVE_DOPRNT 1" >>confdefs.h + +fi + +fi +done + + +for ac_func in \ + atexit basename daemon dup2 fdatasync ffs getopt_long hasmntopt \ + mbsinit memmove memset realpath regcomp setlocale setxattr \ + strcasecmp strchr strdup strerror strnlen strsep strtol strtoul \ + sysconf utime utimensat gettimeofday clock_gettime fork memcpy random snprintf \ + +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +# Check whether --enable-largefile was given. +if test "${enable_largefile+set}" = set; then : + enableval=$enable_largefile; +fi + +if test "$enable_largefile" != no; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 +$as_echo_n "checking for special C compiler options needed for large files... " >&6; } +if ${ac_cv_sys_largefile_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + break +fi +rm -f core conftest.err conftest.$ac_objext + CC="$CC -n32" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_largefile_CC=' -n32'; break +fi +rm -f core conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 +$as_echo "$ac_cv_sys_largefile_CC" >&6; } + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if ${ac_cv_sys_file_offset_bits+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include <sys/types.h> + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=64; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_file_offset_bits=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 +$as_echo "$ac_cv_sys_file_offset_bits" >&6; } +case $ac_cv_sys_file_offset_bits in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF +;; +esac +rm -rf conftest* + if test $ac_cv_sys_file_offset_bits = unknown; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 +$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } +if ${ac_cv_sys_large_files+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <sys/types.h> + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include <sys/types.h> + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=1; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_large_files=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 +$as_echo "$ac_cv_sys_large_files" >&6; } +case $ac_cv_sys_large_files in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF +;; +esac +rm -rf conftest* + fi + + +fi + + +if test "$GCC" = "yes" ; then + # We add -Wall to enable some compiler warnings. + CFLAGS="${CFLAGS} -Wall" +fi + +if test "${enable_pedantic}" = "yes"; then + enable_warnings="yes" + CFLAGS="${CFLAGS} -pedantic" +fi + +if test "${enable_warnings}" = "yes"; then + CFLAGS="${CFLAGS} -W -Wall -Waggregate-return -Wbad-function-cast -Wcast-align -Wcast-qual -Wdisabled-optimization -Wdiv-by-zero -Wfloat-equal -Winline -Wmissing-declarations -Wmissing-format-attribute -Wmissing-noreturn -Wmissing-prototypes -Wmultichar -Wnested-externs -Wpointer-arith -Wredundant-decls -Wshadow -Wsign-compare -Wstrict-prototypes -Wundef -Wwrite-strings -Wformat -Wformat-security -Wuninitialized" +fi + +if test "${enable_debug}" = "yes"; then + CFLAGS="${CFLAGS} -ggdb3 -DDEBUG" + +$as_echo "#define ENABLE_DEBUG 1" >>confdefs.h + +fi + +test "${enable_device_default_io_ops}" = "no" && +$as_echo "#define NO_NTFS_DEVICE_DEFAULT_IO_OPS 1" >>confdefs.h + + +test "${enable_mtab}" = "no" && +$as_echo "#define IGNORE_MTAB 1" >>confdefs.h + +test "${enable_posix_acls}" != "no" && +$as_echo "#define POSIXACLS 1" >>confdefs.h + +test "${enable_xattr_mappings}" != "no" && +$as_echo "#define XATTR_MAPPINGS 1" >>confdefs.h + + +test "${enable_really_static}" = "yes" && enable_library="no" +test "${enable_library}" = "no" && enable_ldconfig="no" + +if test "x${DISTCHECK_HACK}" != "x"; then + enable_mount_helper="no" + enable_ldconfig="no" +fi + +# Settings +pkgconfigdir="\$(libdir)/pkgconfig" +ntfs3gincludedir="\$(includedir)/ntfs-3g" +# Executables should be installed to the root filesystem, otherwise +# automounting NTFS volumes can fail during boot if the driver binaries +# and their dependencies are on an unmounted partition. Use --exec-prefix +# to override this. +if test "x${exec_prefix}" = "xNONE"; then + rootbindir="/bin" + rootsbindir="/sbin" + rootlibdir="/lib${libdir##*/lib}" +else + rootbindir="\$(bindir)" + rootsbindir="\$(sbindir)" + rootlibdir="\$(libdir)" +fi + + + + + + + + + + + + + + + if test "${with_fuse}" = "internal"; then + FUSE_INTERNAL_TRUE= + FUSE_INTERNAL_FALSE='#' +else + FUSE_INTERNAL_TRUE='#' + FUSE_INTERNAL_FALSE= +fi + + if test "${enable_ldscript}" = "yes"; then + GENERATE_LDSCRIPT_TRUE= + GENERATE_LDSCRIPT_FALSE='#' +else + GENERATE_LDSCRIPT_TRUE='#' + GENERATE_LDSCRIPT_FALSE= +fi + + if test "${WINDOWS}" = "yes"; then + WINDOWS_TRUE= + WINDOWS_FALSE='#' +else + WINDOWS_TRUE='#' + WINDOWS_FALSE= +fi + + if test "${enable_device_default_io_ops}" = "yes"; then + NTFS_DEVICE_DEFAULT_IO_OPS_TRUE= + NTFS_DEVICE_DEFAULT_IO_OPS_FALSE='#' +else + NTFS_DEVICE_DEFAULT_IO_OPS_TRUE='#' + NTFS_DEVICE_DEFAULT_IO_OPS_FALSE= +fi + + if test "${enable_ldconfig}" = "yes"; then + RUN_LDCONFIG_TRUE= + RUN_LDCONFIG_FALSE='#' +else + RUN_LDCONFIG_TRUE='#' + RUN_LDCONFIG_FALSE= +fi + + if test "${enable_really_static}" = "yes"; then + REALLYSTATIC_TRUE= + REALLYSTATIC_FALSE='#' +else + REALLYSTATIC_TRUE='#' + REALLYSTATIC_FALSE= +fi + + if test "${enable_library}" = "yes"; then + INSTALL_LIBRARY_TRUE= + INSTALL_LIBRARY_FALSE='#' +else + INSTALL_LIBRARY_TRUE='#' + INSTALL_LIBRARY_FALSE= +fi + + if test "${enable_mount_helper}" = "yes"; then + ENABLE_MOUNT_HELPER_TRUE= + ENABLE_MOUNT_HELPER_FALSE='#' +else + ENABLE_MOUNT_HELPER_TRUE='#' + ENABLE_MOUNT_HELPER_FALSE= +fi + + if test "${enable_ntfs_3g}" = "yes"; then + ENABLE_NTFS_3G_TRUE= + ENABLE_NTFS_3G_FALSE='#' +else + ENABLE_NTFS_3G_TRUE='#' + ENABLE_NTFS_3G_FALSE= +fi + + if test "${enable_ntfsprogs}" = "yes"; then + ENABLE_NTFSPROGS_TRUE= + ENABLE_NTFSPROGS_FALSE='#' +else + ENABLE_NTFSPROGS_TRUE='#' + ENABLE_NTFSPROGS_FALSE= +fi + + if test "${enable_extras}" = "yes"; then + ENABLE_EXTRAS_TRUE= + ENABLE_EXTRAS_FALSE='#' +else + ENABLE_EXTRAS_TRUE='#' + ENABLE_EXTRAS_FALSE= +fi + + if test "${enable_quarantined}" = "yes"; then + ENABLE_QUARANTINED_TRUE= + ENABLE_QUARANTINED_FALSE='#' +else + ENABLE_QUARANTINED_TRUE='#' + ENABLE_QUARANTINED_FALSE= +fi + + +# workaround for <autoconf-2.60 +if test -z "${docdir}"; then + docdir="\$(datarootdir)/doc/\$(PACKAGE_NAME)" + +fi +# workaround for <automake-1.10 +if test -z "${MKDIR_P}"; then + MKDIR_P="\$(mkdir_p)" + +fi + +# generate files +ac_config_files="$ac_config_files Makefile include/Makefile include/fuse-lite/Makefile include/ntfs-3g/Makefile libfuse-lite/Makefile libntfs-3g/Makefile libntfs-3g/libntfs-3g.pc libntfs-3g/libntfs-3g.script.so ntfsprogs/Makefile ntfsprogs/mkntfs.8 ntfsprogs/ntfscat.8 ntfsprogs/ntfsclone.8 ntfsprogs/ntfscluster.8 ntfsprogs/ntfscmp.8 ntfsprogs/ntfscp.8 ntfsprogs/ntfsfix.8 ntfsprogs/ntfsinfo.8 ntfsprogs/ntfslabel.8 ntfsprogs/ntfsls.8 ntfsprogs/ntfsprogs.8 ntfsprogs/ntfsresize.8 ntfsprogs/ntfsundelete.8 ntfsprogs/ntfsdecrypt.8 ntfsprogs/ntfswipe.8 ntfsprogs/ntfstruncate.8 ntfsprogs/ntfsfallocate.8 src/Makefile src/ntfs-3g.8 src/ntfs-3g.probe.8 src/ntfs-3g.usermap.8 src/ntfs-3g.secaudit.8" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_CRYPTO_TRUE}" && test -z "${ENABLE_CRYPTO_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_CRYPTO\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +if test -z "${FUSE_INTERNAL_TRUE}" && test -z "${FUSE_INTERNAL_FALSE}"; then + as_fn_error $? "conditional \"FUSE_INTERNAL\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${GENERATE_LDSCRIPT_TRUE}" && test -z "${GENERATE_LDSCRIPT_FALSE}"; then + as_fn_error $? "conditional \"GENERATE_LDSCRIPT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${WINDOWS_TRUE}" && test -z "${WINDOWS_FALSE}"; then + as_fn_error $? "conditional \"WINDOWS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${NTFS_DEVICE_DEFAULT_IO_OPS_TRUE}" && test -z "${NTFS_DEVICE_DEFAULT_IO_OPS_FALSE}"; then + as_fn_error $? "conditional \"NTFS_DEVICE_DEFAULT_IO_OPS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${RUN_LDCONFIG_TRUE}" && test -z "${RUN_LDCONFIG_FALSE}"; then + as_fn_error $? "conditional \"RUN_LDCONFIG\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${REALLYSTATIC_TRUE}" && test -z "${REALLYSTATIC_FALSE}"; then + as_fn_error $? "conditional \"REALLYSTATIC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${INSTALL_LIBRARY_TRUE}" && test -z "${INSTALL_LIBRARY_FALSE}"; then + as_fn_error $? "conditional \"INSTALL_LIBRARY\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_MOUNT_HELPER_TRUE}" && test -z "${ENABLE_MOUNT_HELPER_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_MOUNT_HELPER\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_NTFS_3G_TRUE}" && test -z "${ENABLE_NTFS_3G_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_NTFS_3G\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_NTFSPROGS_TRUE}" && test -z "${ENABLE_NTFSPROGS_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_NTFSPROGS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_EXTRAS_TRUE}" && test -z "${ENABLE_EXTRAS_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_EXTRAS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_QUARANTINED_TRUE}" && test -z "${ENABLE_QUARANTINED_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_QUARANTINED\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by ntfs-3g $as_me 2015.3.14, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to <ntfs-3g-devel@lists.sf.net>." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +ntfs-3g config.status 2015.3.14 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + + +# 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 + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' +macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' +enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' +enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' +pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' +enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' +SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' +ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' +PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' +host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' +host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' +host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' +build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' +build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' +build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' +SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' +Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' +GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' +EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' +FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' +LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' +NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' +LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' +max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' +ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' +exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' +lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' +lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' +lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' +lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' +lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' +reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' +reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' +OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' +deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' +file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' +file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' +want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' +DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' +sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' +AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' +AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' +archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' +STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' +RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' +old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' +old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' +lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' +CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' +CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' +compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' +GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' +nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' +lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' +objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' +MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' +need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' +MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' +DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' +NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' +LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' +OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' +OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' +libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' +shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' +extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' +compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' +module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' +with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' +no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' +hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' +hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' +inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' +link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' +always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' +exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' +include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' +prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' +postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' +file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' +variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' +need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' +need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' +version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' +runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' +libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' +library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' +soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' +install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' +postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' +postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' +finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' +hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' +sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' +sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`' +hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' +enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' +old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' +striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' + +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in SHELL \ +ECHO \ +PATH_SEPARATOR \ +SED \ +GREP \ +EGREP \ +FGREP \ +LD \ +NM \ +LN_S \ +lt_SP2NL \ +lt_NL2SP \ +reload_flag \ +OBJDUMP \ +deplibs_check_method \ +file_magic_cmd \ +file_magic_glob \ +want_nocaseglob \ +DLLTOOL \ +sharedlib_from_linklib_cmd \ +AR \ +AR_FLAGS \ +archiver_list_spec \ +STRIP \ +RANLIB \ +CC \ +CFLAGS \ +compiler \ +lt_cv_sys_global_symbol_pipe \ +lt_cv_sys_global_symbol_to_cdecl \ +lt_cv_sys_global_symbol_to_c_name_address \ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ +nm_file_list_spec \ +lt_prog_compiler_no_builtin_flag \ +lt_prog_compiler_pic \ +lt_prog_compiler_wl \ +lt_prog_compiler_static \ +lt_cv_prog_compiler_c_o \ +need_locks \ +MANIFEST_TOOL \ +DSYMUTIL \ +NMEDIT \ +LIPO \ +OTOOL \ +OTOOL64 \ +shrext_cmds \ +export_dynamic_flag_spec \ +whole_archive_flag_spec \ +compiler_needs_object \ +with_gnu_ld \ +allow_undefined_flag \ +no_undefined_flag \ +hardcode_libdir_flag_spec \ +hardcode_libdir_separator \ +exclude_expsyms \ +include_expsyms \ +file_list_spec \ +variables_saved_for_relink \ +libname_spec \ +library_names_spec \ +soname_spec \ +install_override_mode \ +finish_eval \ +old_striplib \ +striplib; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds \ +old_postinstall_cmds \ +old_postuninstall_cmds \ +old_archive_cmds \ +extract_expsyms_cmds \ +old_archive_from_new_cmds \ +old_archive_from_expsyms_cmds \ +archive_cmds \ +archive_expsym_cmds \ +module_cmds \ +module_expsym_cmds \ +export_symbols_cmds \ +prelink_cmds \ +postlink_cmds \ +postinstall_cmds \ +postuninstall_cmds \ +finish_cmds \ +sys_lib_search_path_spec \ +sys_lib_dlsearch_path_spec; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +ac_aux_dir='$ac_aux_dir' +xsi_shell='$xsi_shell' +lt_shell_append='$lt_shell_append' + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile' + + + + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; + "include/fuse-lite/Makefile") CONFIG_FILES="$CONFIG_FILES include/fuse-lite/Makefile" ;; + "include/ntfs-3g/Makefile") CONFIG_FILES="$CONFIG_FILES include/ntfs-3g/Makefile" ;; + "libfuse-lite/Makefile") CONFIG_FILES="$CONFIG_FILES libfuse-lite/Makefile" ;; + "libntfs-3g/Makefile") CONFIG_FILES="$CONFIG_FILES libntfs-3g/Makefile" ;; + "libntfs-3g/libntfs-3g.pc") CONFIG_FILES="$CONFIG_FILES libntfs-3g/libntfs-3g.pc" ;; + "libntfs-3g/libntfs-3g.script.so") CONFIG_FILES="$CONFIG_FILES libntfs-3g/libntfs-3g.script.so" ;; + "ntfsprogs/Makefile") CONFIG_FILES="$CONFIG_FILES ntfsprogs/Makefile" ;; + "ntfsprogs/mkntfs.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/mkntfs.8" ;; + "ntfsprogs/ntfscat.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfscat.8" ;; + "ntfsprogs/ntfsclone.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsclone.8" ;; + "ntfsprogs/ntfscluster.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfscluster.8" ;; + "ntfsprogs/ntfscmp.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfscmp.8" ;; + "ntfsprogs/ntfscp.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfscp.8" ;; + "ntfsprogs/ntfsfix.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsfix.8" ;; + "ntfsprogs/ntfsinfo.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsinfo.8" ;; + "ntfsprogs/ntfslabel.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfslabel.8" ;; + "ntfsprogs/ntfsls.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsls.8" ;; + "ntfsprogs/ntfsprogs.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsprogs.8" ;; + "ntfsprogs/ntfsresize.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsresize.8" ;; + "ntfsprogs/ntfsundelete.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsundelete.8" ;; + "ntfsprogs/ntfsdecrypt.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsdecrypt.8" ;; + "ntfsprogs/ntfswipe.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfswipe.8" ;; + "ntfsprogs/ntfstruncate.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfstruncate.8" ;; + "ntfsprogs/ntfsfallocate.8") CONFIG_FILES="$CONFIG_FILES ntfsprogs/ntfsfallocate.8" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "src/ntfs-3g.8") CONFIG_FILES="$CONFIG_FILES src/ntfs-3g.8" ;; + "src/ntfs-3g.probe.8") CONFIG_FILES="$CONFIG_FILES src/ntfs-3g.probe.8" ;; + "src/ntfs-3g.usermap.8") CONFIG_FILES="$CONFIG_FILES src/ntfs-3g.usermap.8" ;; + "src/ntfs-3g.secaudit.8") CONFIG_FILES="$CONFIG_FILES src/ntfs-3g.secaudit.8" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' <conf$$subs.awk | sed ' +/^[^""]/{ + N + s/\n// +} +' >>$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' <confdefs.h | sed ' +s/'"$ac_delim"'/"\\\ +"/g' >>$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + "libtool":C) + + # 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 + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool 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. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# The names of the tagged configurations supported by this script. +available_tags="" + +# ### BEGIN LIBTOOL CONFIG + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The PATH separator for the build system. +PATH_SEPARATOR=$lt_PATH_SEPARATOR + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# convert \$build file names to \$host format. +to_host_file_cmd=$lt_cv_to_host_file_cmd + +# convert \$build files to toolchain format. +to_tool_file_cmd=$lt_cv_to_tool_file_cmd + +# An object symbol dumper. +OBJDUMP=$lt_OBJDUMP + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method = "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# How to find potential files when deplibs_check_method = "file_magic". +file_magic_glob=$lt_file_magic_glob + +# Find potential files using nocaseglob when deplibs_check_method = "file_magic". +want_nocaseglob=$lt_want_nocaseglob + +# DLL creation program. +DLLTOOL=$lt_DLLTOOL + +# Command to associate shared and link libraries. +sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd + +# The archiver. +AR=$lt_AR + +# Flags to create an archive. +AR_FLAGS=$lt_AR_FLAGS + +# How to feed a file listing to the archiver. +archiver_list_spec=$lt_archiver_list_spec + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# Specify filename containing input files for \$NM. +nm_file_list_spec=$lt_nm_file_list_spec + +# The root where to search for dependent libraries,and in which our libraries should be installed. +lt_sysroot=$lt_sysroot + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Manifest tool. +MANIFEST_TOOL=$lt_MANIFEST_TOOL + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain="$ac_aux_dir/ltmain.sh" + + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + if test x"$xsi_shell" = xyes; then + sed -e '/^func_dirname ()$/,/^} # func_dirname /c\ +func_dirname ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_basename ()$/,/^} # func_basename /c\ +func_basename ()\ +{\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\ +func_dirname_and_basename ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_stripname ()$/,/^} # func_stripname /c\ +func_stripname ()\ +{\ +\ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\ +\ # positional parameters, so assign one to ordinary parameter first.\ +\ func_stripname_result=${3}\ +\ func_stripname_result=${func_stripname_result#"${1}"}\ +\ func_stripname_result=${func_stripname_result%"${2}"}\ +} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\ +func_split_long_opt ()\ +{\ +\ func_split_long_opt_name=${1%%=*}\ +\ func_split_long_opt_arg=${1#*=}\ +} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\ +func_split_short_opt ()\ +{\ +\ func_split_short_opt_arg=${1#??}\ +\ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\ +} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\ +func_lo2o ()\ +{\ +\ case ${1} in\ +\ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\ +\ *) func_lo2o_result=${1} ;;\ +\ esac\ +} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_xform ()$/,/^} # func_xform /c\ +func_xform ()\ +{\ + func_xform_result=${1%.*}.lo\ +} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_arith ()$/,/^} # func_arith /c\ +func_arith ()\ +{\ + func_arith_result=$(( $* ))\ +} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_len ()$/,/^} # func_len /c\ +func_len ()\ +{\ + func_len_result=${#1}\ +} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + +fi + +if test x"$lt_shell_append" = xyes; then + sed -e '/^func_append ()$/,/^} # func_append /c\ +func_append ()\ +{\ + eval "${1}+=\\${2}"\ +} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\ +func_append_quoted ()\ +{\ +\ func_quote_for_eval "${2}"\ +\ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\ +} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + # Save a `func_append' function call where possible by direct use of '+=' + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +else + # Save a `func_append' function call even when '+=' is not available + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +fi + +if test x"$_lt_function_replace_fail" = x":"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5 +$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;} +fi + + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" + + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + +if test "${with_fuse}" = "external"; then + if ! echo "x$FUSE_LIB_PATH" | grep -- "x-L/lib" > /dev/null; then + cat <<EOF +**************************************************************************** +* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * +* The FUSE user space binaries were NOT installed with root directory * +* executable prefix. This means that automounting NTFS volumes during boot * +* could fail. This can be fixed the below way by reinstalling FUSE using * +* the right 'configure' option during FUSE compilation: * +* ./configure --exec-prefix=/ * +* make && sudo make install * +* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * +**************************************************************************** +EOF + fi +fi + +echo "You can type now 'make' to build ntfs-3g." + diff --git a/configure.ac b/configure.ac new file mode 100755 index 0000000000000000000000000000000000000000..6f293784b369b7a2c0f561a9424fb8f27b211491 --- /dev/null +++ b/configure.ac @@ -0,0 +1,686 @@ +# +# configure.ac - Source file to generate "./configure" to prepare package for +# compilation. +# +# Copyright (c) 2000-2013 Anton Altaparmakov +# Copyright (c) 2003 Jan Kratochvil +# Copyright (c) 2005-2009 Szabolcs Szakacsits +# Copyright (C) 2007-2008 Alon Bar-Lev +# +# This program/include file 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/include file 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 (in the main directory of the NTFS-3G +# distribution in the file COPYING); if not, write to the Free Software +# Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Autoconf +AC_PREREQ(2.59) +AC_INIT([ntfs-3g],[2015.3.14],[ntfs-3g-devel@lists.sf.net]) +LIBNTFS_3G_VERSION="86" +AC_CONFIG_SRCDIR([src/ntfs-3g.c]) + +# Environment +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +# Automake +AM_INIT_AUTOMAKE([${PACKAGE_NAME}], [${PACKAGE_VERSION}]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) +AM_MAINTAINER_MODE + +# Options +AC_ARG_ENABLE( + [debug], + [AS_HELP_STRING([--enable-debug],[enable debugging code and output])], + , + [enable_debug="no"] +) + +AC_ARG_ENABLE( + [warnings], + [AS_HELP_STRING([--enable-warnings],[enable lots of compiler warnings])], + , + [enable_warnings="no"] +) + +AC_ARG_ENABLE( + [pedantic], + [AS_HELP_STRING([--enable-pedantic],[enable compile pedantic mode])], + , + [enable_pedantic="no"] +) + +AC_ARG_ENABLE( + [really-static], + [AS_HELP_STRING([--enable-really-static],[create fully static binaries])], + , + [enable_really_static="no"] +) + +AC_ARG_ENABLE( + [mount-helper], + [AS_HELP_STRING([--enable-mount-helper],[install mount helper @<:@default=enabled for linux@:>@])], + , + [ + case "${target_os}" in + linux*) enable_mount_helper="yes" ;; + *) enable_mount_helper="no" ;; + esac + ] +) + +AC_ARG_ENABLE( + [ldscript], + [AS_HELP_STRING([--enable-ldscript],[use ldscript instead of .so symlink])], + , + [enable_ldscript="no"] +) + +AC_ARG_ENABLE( + [ldconfig], + [AS_HELP_STRING([--disable-ldconfig],[do not update dynamic linker cache using ldconfig])], + , + [enable_ldconfig="yes"] +) + +AC_ARG_ENABLE( + [library], + [AS_HELP_STRING([--disable-library],[do not install libntfs-3g but link it into ntfs-3g])], + , + [enable_library="yes"] +) + +AC_ARG_ENABLE( + [mtab], + [AS_HELP_STRING([--disable-mtab],[disable and ignore usage of /etc/mtab])], + , + [enable_mtab="yes"] +) + +AC_ARG_ENABLE( + [posix-acls], + [AS_HELP_STRING([--enable-posix-acls],[enable POSIX ACL support])], + , + [enable_posix_acls="no"] +) + +AC_ARG_ENABLE( + [xattr-mappings], + [AS_HELP_STRING([--enable-xattr-mappings],[enable system extended attributes mappings])], + , + [enable_xattr_mappings="no"] +) + +AC_ARG_ENABLE( + [device-default-io-ops], + [AS_HELP_STRING([--disable-device-default-io-ops],[install default IO ops])], + , + [enable_device_default_io_ops="yes"] +) + +AC_ARG_ENABLE( + [ntfs-3g], + [AS_HELP_STRING([--disable-ntfs-3g],[disable the ntfs-3g FUSE driver])], + , + [enable_ntfs_3g="yes"] +) + +AC_ARG_ENABLE( + [ntfsprogs], + [AS_HELP_STRING([--disable-ntfsprogs],[disable ntfsprogs utilities + (default=no)])], + , + [enable_ntfsprogs="yes"] +) + +AC_ARG_ENABLE(crypto, + AS_HELP_STRING(--enable-crypto,enable crypto related code and utilities + (default=no)), , + enable_crypto=no +) + +AC_ARG_ENABLE( + [extras], + [AS_HELP_STRING([--enable-extras],[enable extra ntfsprogs utilities + (default=no)])], + , + [enable_extras="no"] +) + +AC_ARG_ENABLE( + [quarantined], + [AS_HELP_STRING([--enable-quarantined],[enable quarantined ntfsprogs utilities + (default=no)])], + , + [enable_quarantined="no"] +) + +AC_ARG_ENABLE( + [nfconv], + [AS_HELP_STRING([--disable-nfconv],[disable the 'nfconv' patch, which adds support for Unicode normalization form conversion when built on Mac OS X @<:@default=enabled for Mac OS X@:>@])], + [enable_nfconv="no"], + [ + case "${target_os}" in + darwin*) enable_nfconv="yes" ;; + *) enable_nfconv="no" ;; + esac + ] +) + +# pthread_rwlock_t requires _GNU_SOURCE +AC_GNU_SOURCE + +# Programs +AC_PROG_CC(gcc cc) +AC_PROG_LN_S +AM_PROG_CC_C_O + +ifdef( + [LT_INIT], + [LT_INIT], + [AC_PROG_LIBTOOL] +) + +AC_PROG_INSTALL +PKG_PROG_PKG_CONFIG + +AC_PATH_PROG([MV], [mv]) +AC_PATH_PROG([RM], [rm]) +AC_PATH_PROG([SED], [sed]) +AC_ARG_VAR([LDCONFIG], [ldconfig utility]) +AC_PATH_PROG([LDCONFIG], [ldconfig], [true], [/sbin /usr/sbin $PATH]) + +# Environment +AC_MSG_CHECKING([Windows OS]) +case "${target}" in +*-mingw32*|*-winnt*|*-cygwin*) + AC_MSG_RESULT([yes]) + WINDOWS="yes" + AC_DEFINE( + [WINDOWS], + [1], + [Define to 1 if this is a Windows OS] + ) + ;; +*) + AC_MSG_RESULT([no]) + WINDOWS="no" + ;; +esac + +if test "x${enable_ntfs_3g}" = "xyes"; then + AC_MSG_CHECKING([fuse compatibility]) + case "${target_os}" in + linux*|solaris*) + AC_ARG_WITH( + [fuse], + [AS_HELP_STRING([--with-fuse=<internal|external>],[Select FUSE library: internal or external @<:@default=internal@:>@])], + , + [with_fuse="internal"] + ) + ;; + darwin*|netbsd*|kfreebsd*-gnu) + with_fuse="external" + ;; + freebsd*) + AC_MSG_ERROR([Please see FreeBSD support at http://www.freshports.org/sysutils/fusefs-ntfs]) + ;; + *) + AC_MSG_ERROR([ntfs-3g can be built for Linux, FreeBSD, Mac OS X, NetBSD, and Solaris only.]) + ;; + esac + AC_MSG_RESULT([${with_fuse}]) +else + with_fuse="none" +fi + +case "${target_os}" in +solaris*) + if test "x$GCC" != "xyes" ; then + AC_MSG_ERROR([ntfs-3g can be built only with gcc on Solaris. Install it by 'pkg install gcc-dev' and retry.)]) + fi + ;; +esac + +if test "${enable_ldscript}" = "yes"; then + AC_MSG_CHECKING([Output format]) + OUTPUT_FORMAT="$(${CC} ${CFLAGS} ${LDFLAGS} -Wl,--verbose 2>&1 | ${SED} -n 's/^OUTPUT_FORMAT("\([[^"]]*\)",.*/\1/p')" + if test -z "${OUTPUT_FORMAT}"; then + AC_MSG_RESULT([None]) + else + AC_MSG_RESULT([${OUTPUT_FORMAT}]) + OUTPUT_FORMAT="OUTPUT_FORMAT ( ${OUTPUT_FORMAT} )" + fi +fi + +# Libraries +if test "${with_fuse}" = "internal"; then + AC_CHECK_LIB( + [pthread], + [pthread_create], + [LIBFUSE_LITE_LIBS="${LIBFUSE_LITE_LIBS} -lpthread"], + [AC_MSG_ERROR([Cannot find pthread library])] + ) + AC_DEFINE( + [_REENTRANT], + [1], + [Required define if using POSIX threads] + ) + # required so that we re-compile anything + AC_DEFINE( + [FUSE_INTERNAL], + [1], + [Define to 1 if using internal fuse] + ) + + AC_MSG_CHECKING([Solaris OS]) + AC_LANG_PUSH([C]) + AC_COMPILE_IFELSE( + [ + AC_LANG_SOURCE( + [[#if !((defined(sun) || defined(__sun)) && (defined(__SVR4) || defined(__svr4__)))]] + [[#error "Not a Solaris system."]] + [[#endif]] + ) + ], + [ + AC_MSG_RESULT([yes]) + LIBFUSE_LITE_CFLAGS="${LIBFUSE_LITE_CFLAGS} -std=c99 -D__SOLARIS__ -D_XOPEN_SOURCE=600 -D__EXTENSIONS__" + LIBFUSE_LITE_LIBS="${LIBFUSE_LITE_LIBS} -lxnet" + ], + [ + AC_MSG_RESULT([no]) + ] + ) + AC_LANG_POP([C]) +elif test "${with_fuse}" = "external"; then + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + test "x${PKG_CONFIG}" = "xno" && AC_MSG_ERROR([pkg-config wasn't found! Please install from your vendor, or see http://pkg-config.freedesktop.org/wiki/]) + # Libraries often install their metadata .pc files in directories + # not searched by pkg-config. Let's workaround this. + export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/lib/pkgconfig:/usr/lib/pkgconfig:/opt/gnome/lib/pkgconfig:/usr/share/pkgconfig:/usr/local/lib/pkgconfig:$prefix/lib/pkgconfig:/opt/gnome/share/pkgconfig:/usr/local/share/pkgconfig + PKG_CHECK_MODULES( + [FUSE_MODULE], + [fuse >= 2.6.0], + , + [ + AC_MSG_ERROR([FUSE >= 2.6.0 was not found. Either older FUSE is still present, or FUSE is not fully installed (e.g. fuse, libfuse, libfuse2, libfuse-dev, etc packages). Source code: http://fuse.sf.net]) + ] + ) + FUSE_LIB_PATH=`$PKG_CONFIG --libs-only-L fuse | sed -e 's,/[/]*,/,g' -e 's,[ ]*$,,'` +fi + +# Autodetect whether we can build crypto stuff or not. +compile_crypto=false +if test "$enable_crypto" != "no"; then + have_libgcrypt=false + AM_PATH_LIBGCRYPT(1.2.2, [ have_libgcrypt=true ], + [ + if test "$enable_crypto" = "yes"; then + AC_MSG_ERROR([ntfsprogs crypto code requires the gcrypt library.]) + else + AC_MSG_WARN([ntfsprogs crypto code requires the gcrypt library.]) + fi + ]) + have_libgnutls=false + PKG_CHECK_MODULES(GNUTLS, gnutls >= 1.4.4, [ have_libgnutls=true ], + if test "$enable_crypto" = "yes"; then + AC_MSG_ERROR([ntfsprogs crypto code requires the gnutls library.]) + else + AC_MSG_WARN([ntfsprogs crypto code requires the gnutls library.]) + fi + ) + if test "$have_libgcrypt" = "true"; then + if test "$have_libgnutls" = "true"; then + compile_crypto=true + AC_DEFINE([ENABLE_CRYPTO], 1, + [Define this to 1 if you want to enable support of + encrypted files in libntfs and utilities.]) + fi + fi +fi +AM_CONDITIONAL(ENABLE_CRYPTO, $compile_crypto) + +# add --with-extra-includes and --with-extra-libs switch to ./configure +all_libraries="$all_libraries $USER_LDFLAGS" +all_includes="$all_includes $USER_INCLUDES" +AC_SUBST(all_includes) +AC_SUBST(all_libraries) + +# Specify support for generating DCE compliant UUIDs (aka GUIDs). We check if +# uuid/uuid.h header is present and the uuid library is present that goes with +# it and then check if uuid_generate() is present and usable. +# +# DCE UUIDs are enabled by default and can be disabled with the --disable-uuid +# option to the configure script. +AC_ARG_WITH(uuid, [ + --with-uuid@<:@=PFX@:>@ generate DCE compliant UUIDs, with optional prefix + to uuid library and headers @<:@default=detect@:>@ + --without-uuid do not generate DCE compliant UUIDs], + if test "$with_uuid" = "yes"; then + extrapath=default + elif test "$with_uuid" = "no"; then + extrapath= + else + extrapath=$with_uuid + fi, + extrapath=default +) +if test "x$extrapath" != "x"; then + if test "x$extrapath" != "xdefault"; then + MKNTFS_CPPFLAGS="$MKNTFS_CPPFLAGS -I$extrapath/include" + MKNTFS_LIBS="$MKNTFS_LIBS -L$extrapath/lib" + fi + + search_for_luuid="yes" + AC_CHECK_HEADER([uuid/uuid.h], + [], + [ + AC_MSG_WARN([ntfsprogs DCE compliant UUID generation code requires the uuid library.]) + search_for_luuid="no" + ], + ) + + if test "x$search_for_luuid" != "xno"; then + # Look for uuid_generate in the standard C library. + AC_CHECK_FUNC([uuid_generate], + [ + AC_DEFINE([ENABLE_UUID], 1, + [Define this to 1 if you want to enable + generation of DCE compliant UUIDs.]) + search_for_luuid="no" + ], + [], + ) + fi + + if test "x$search_for_luuid" != "xno"; then + # Look for uuid_generate in the 'uuid' library. + AC_CHECK_LIB([uuid], [uuid_generate], + [ + AC_DEFINE([ENABLE_UUID], 1, + [Define this to 1 if you want to enable + generation of DCE compliant UUIDs.]) + MKNTFS_LIBS="$MKNTFS_LIBS -luuid" + search_for_luuid="no" + ], + [], + ) + fi + + if test "x$search_for_luuid" != "xno"; then + AC_MSG_WARN([ntfsprogs DCE compliant UUID generation code requires the uuid library.]) + fi +fi + +# Specify support for obtaining the correct BIOS legacy geometry needed for +# Windows to boot in CHS mode. We check if hd.h header is present and the hd +# library is present that goes with it and then check if the hd_list() function +# is present and usable. +# +# Using the hd library is enabled by default and can be disabled with the +# --disable-hd option to the configure script. +AC_ARG_WITH(hd, [ + --with-hd@<:@=PFX@:>@ use Windows compliant disk geometry, with optional + prefix to hd library and headers @<:@default=detect@:>@ + --without-hd do not use Windows compliant disk geometry], + if test "$with_hd" = "yes"; then + extrapath2=default + elif test "$with_hd" = "no"; then + extrapath2= + else + extrapath2=$with_hd + fi, + extrapath2=default +) +if test "x$extrapath2" != "x"; then + if test "x$extrapath2" != "xdefault"; then + LIBNTFS_CPPFLAGS="$LIBNTFS_CPPFLAGS -I$extrapath2/include" + LIBNTFS_LIBS="$LIBNTFS_LIBS -L$extrapath2/lib" + fi + AC_CHECK_HEADER([hd.h], + AC_CHECK_LIB([hd], [hd_list], + AC_DEFINE([ENABLE_HD], 1, + [Define this to 1 if you want to enable use of Windows + compliant disk geometry.]) + LIBNTFS_LIBS="$LIBNTFS_LIBS -lhd" + NTFSPROGS_STATIC_LIBS="$NTFSPROGS_STATIC_LIBS -lhd", + AC_MSG_WARN([ntfsprogs Windows compliant geometry code requires the hd library.]), + ), + AC_MSG_WARN([ntfsprogs Windows compliant geometry code requires the hd library.]), + ) +fi + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \ + mntent.h stddef.h stdint.h stdlib.h stdio.h stdarg.h string.h \ + strings.h errno.h time.h unistd.h utime.h wchar.h getopt.h features.h \ + regex.h endian.h byteswap.h sys/byteorder.h sys/disk.h sys/endian.h \ + sys/param.h sys/ioctl.h sys/mkdev.h sys/mount.h sys/stat.h sys/types.h \ + sys/vfs.h sys/statvfs.h sys/sysmacros.h linux/major.h linux/fd.h \ + linux/fs.h inttypes.h linux/hdreg.h \ + machine/endian.h windows.h syslog.h pwd.h malloc.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_BIGENDIAN( + , + [ + AC_DEFINE( + [WORDS_LITTLEENDIAN], + [1], + [Define to 1 if your processor stores words with the least significant + byte first (like Intel and VAX, unlike Motorola and SPARC).] + ) + ] + , +) +AC_C_CONST +AC_C_INLINE +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_STRUCT_ST_BLOCKS +AC_CHECK_MEMBERS([struct stat.st_rdev]) +AC_CHECK_MEMBERS([struct stat.st_atim]) +AC_CHECK_MEMBERS([struct stat.st_atimespec]) +AC_CHECK_MEMBERS([struct stat.st_atimensec]) + +# For the 'nfconv' patch (Mac OS X only): +case "${target_os}" in +darwin*) + if test "${enable_nfconv}" = "yes"; then + AC_CHECK_HEADER( + [CoreFoundation/CoreFoundation.h], + [ + LDFLAGS="${LDFLAGS} -framework CoreFoundation" + AC_DEFINE( + [ENABLE_NFCONV], + [1], + [Define to 1 if the nfconv patch should be enabled] + ) + ], + AC_MSG_ERROR([[Cannot find CoreFoundation required for 'nfconv' functionality Mac OS X. You may use the --disable-nfconv 'configure' option to avoid this error.]]) + ) + fi + ;; +esac + +# Checks for library functions. +AC_FUNC_GETMNTENT +AC_FUNC_MBRTOWC +AC_FUNC_MEMCMP +AC_FUNC_STAT +AC_FUNC_STRFTIME +AC_FUNC_UTIME_NULL +AC_FUNC_VPRINTF +AC_CHECK_FUNCS([ \ + atexit basename daemon dup2 fdatasync ffs getopt_long hasmntopt \ + mbsinit memmove memset realpath regcomp setlocale setxattr \ + strcasecmp strchr strdup strerror strnlen strsep strtol strtoul \ + sysconf utime utimensat gettimeofday clock_gettime fork memcpy random snprintf \ +]) +AC_SYS_LARGEFILE + +if test "$GCC" = "yes" ; then + # We add -Wall to enable some compiler warnings. + CFLAGS="${CFLAGS} -Wall" +fi + +if test "${enable_pedantic}" = "yes"; then + enable_warnings="yes" + CFLAGS="${CFLAGS} -pedantic" +fi + +if test "${enable_warnings}" = "yes"; then + CFLAGS="${CFLAGS} -W -Wall -Waggregate-return -Wbad-function-cast -Wcast-align -Wcast-qual -Wdisabled-optimization -Wdiv-by-zero -Wfloat-equal -Winline -Wmissing-declarations -Wmissing-format-attribute -Wmissing-noreturn -Wmissing-prototypes -Wmultichar -Wnested-externs -Wpointer-arith -Wredundant-decls -Wshadow -Wsign-compare -Wstrict-prototypes -Wundef -Wwrite-strings -Wformat -Wformat-security -Wuninitialized" +fi + +if test "${enable_debug}" = "yes"; then + CFLAGS="${CFLAGS} -ggdb3 -DDEBUG" + AC_DEFINE( + [ENABLE_DEBUG], + [1], + [Define to 1 if debug should be enabled] + ) +fi + +test "${enable_device_default_io_ops}" = "no" && AC_DEFINE( + [NO_NTFS_DEVICE_DEFAULT_IO_OPS], + [1], + [Don't use default IO ops] +) + +test "${enable_mtab}" = "no" && AC_DEFINE([IGNORE_MTAB], [1], [Don't update /etc/mtab]) +test "${enable_posix_acls}" != "no" && AC_DEFINE([POSIXACLS], [1], [POSIX ACL support]) +test "${enable_xattr_mappings}" != "no" && AC_DEFINE([XATTR_MAPPINGS], [1], [system extended attributes mappings]) + +test "${enable_really_static}" = "yes" && enable_library="no" +test "${enable_library}" = "no" && enable_ldconfig="no" + +if test "x${DISTCHECK_HACK}" != "x"; then + enable_mount_helper="no" + enable_ldconfig="no" +fi + +# Settings +pkgconfigdir="\$(libdir)/pkgconfig" +ntfs3gincludedir="\$(includedir)/ntfs-3g" +# Executables should be installed to the root filesystem, otherwise +# automounting NTFS volumes can fail during boot if the driver binaries +# and their dependencies are on an unmounted partition. Use --exec-prefix +# to override this. +if test "x${exec_prefix}" = "xNONE"; then + rootbindir="/bin" + rootsbindir="/sbin" + rootlibdir="/lib${libdir##*/lib}" +else + rootbindir="\$(bindir)" + rootsbindir="\$(sbindir)" + rootlibdir="\$(libdir)" +fi +AC_SUBST([pkgconfigdir]) +AC_SUBST([ntfs3gincludedir]) +AC_SUBST([rootbindir]) +AC_SUBST([rootsbindir]) +AC_SUBST([rootlibdir]) +AC_SUBST([LIBNTFS_3G_VERSION]) +AC_SUBST([LIBFUSE_LITE_CFLAGS]) +AC_SUBST([LIBFUSE_LITE_LIBS]) +AC_SUBST([MKNTFS_CPPFLAGS]) +AC_SUBST([MKNTFS_LIBS]) +AC_SUBST([LIBNTFS_CPPFLAGS]) +AC_SUBST([LIBNTFS_LIBS]) +AC_SUBST([NTFSPROGS_STATIC_LIBS]) +AC_SUBST([OUTPUT_FORMAT]) +AM_CONDITIONAL([FUSE_INTERNAL], [test "${with_fuse}" = "internal"]) +AM_CONDITIONAL([GENERATE_LDSCRIPT], [test "${enable_ldscript}" = "yes"]) +AM_CONDITIONAL([WINDOWS], [test "${WINDOWS}" = "yes"]) +AM_CONDITIONAL([NTFS_DEVICE_DEFAULT_IO_OPS], [test "${enable_device_default_io_ops}" = "yes"]) +AM_CONDITIONAL([RUN_LDCONFIG], [test "${enable_ldconfig}" = "yes"]) +AM_CONDITIONAL([REALLYSTATIC], [test "${enable_really_static}" = "yes"]) +AM_CONDITIONAL([INSTALL_LIBRARY], [test "${enable_library}" = "yes"]) +AM_CONDITIONAL([ENABLE_MOUNT_HELPER], [test "${enable_mount_helper}" = "yes"]) +AM_CONDITIONAL([ENABLE_NTFS_3G], [test "${enable_ntfs_3g}" = "yes"]) +AM_CONDITIONAL([ENABLE_NTFSPROGS], [test "${enable_ntfsprogs}" = "yes"]) +AM_CONDITIONAL([ENABLE_EXTRAS], [test "${enable_extras}" = "yes"]) +AM_CONDITIONAL([ENABLE_QUARANTINED], [test "${enable_quarantined}" = "yes"]) + +# workaround for <autoconf-2.60 +if test -z "${docdir}"; then + docdir="\$(datarootdir)/doc/\$(PACKAGE_NAME)" + AC_SUBST([docdir]) +fi +# workaround for <automake-1.10 +if test -z "${MKDIR_P}"; then + MKDIR_P="\$(mkdir_p)" + AC_SUBST([MKDIR_P]) +fi + +# generate files +AC_CONFIG_FILES([ + Makefile + include/Makefile + include/fuse-lite/Makefile + include/ntfs-3g/Makefile + libfuse-lite/Makefile + libntfs-3g/Makefile + libntfs-3g/libntfs-3g.pc + libntfs-3g/libntfs-3g.script.so + ntfsprogs/Makefile + ntfsprogs/mkntfs.8 + ntfsprogs/ntfscat.8 + ntfsprogs/ntfsclone.8 + ntfsprogs/ntfscluster.8 + ntfsprogs/ntfscmp.8 + ntfsprogs/ntfscp.8 + ntfsprogs/ntfsfix.8 + ntfsprogs/ntfsinfo.8 + ntfsprogs/ntfslabel.8 + ntfsprogs/ntfsls.8 + ntfsprogs/ntfsprogs.8 + ntfsprogs/ntfsresize.8 + ntfsprogs/ntfsundelete.8 + ntfsprogs/ntfsdecrypt.8 + ntfsprogs/ntfswipe.8 + ntfsprogs/ntfstruncate.8 + ntfsprogs/ntfsfallocate.8 + src/Makefile + src/ntfs-3g.8 + src/ntfs-3g.probe.8 + src/ntfs-3g.usermap.8 + src/ntfs-3g.secaudit.8 +]) +AC_OUTPUT + +if test "${with_fuse}" = "external"; then + if ! echo "x$FUSE_LIB_PATH" | grep -- "x-L/lib" > /dev/null; then + cat <<EOF +**************************************************************************** +* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * +* The FUSE user space binaries were NOT installed with root directory * +* executable prefix. This means that automounting NTFS volumes during boot * +* could fail. This can be fixed the below way by reinstalling FUSE using * +* the right 'configure' option during FUSE compilation: * +* ./configure --exec-prefix=/ * +* make && sudo make install * +* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * +**************************************************************************** +EOF + fi +fi + +echo "You can type now 'make' to build ntfs-3g." + diff --git a/depcomp b/depcomp new file mode 100755 index 0000000000000000000000000000000000000000..4ebd5b3a2f2d689de95251c9424e2763aa159de5 --- /dev/null +++ b/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2013-05-30.07; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. + +# 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, 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 <http://www.gnu.org/licenses/>. + +# 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. + +# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>. + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to <bug-automake@gnu.org>. +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/include/Makefile b/include/Makefile new file mode 100755 index 0000000000000000000000000000000000000000..1790fb91cd1f1fbd008f74459ee2eb65b4faa95a --- /dev/null +++ b/include/Makefile @@ -0,0 +1,639 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# include/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/ntfs-3g +pkgincludedir = $(includedir)/ntfs-3g +pkglibdir = $(libdir)/ntfs-3g +pkglibexecdir = $(libexecdir)/ntfs-3g +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = x86_64-unknown-linux-gnu +host_triplet = x86_64-unknown-linux-gnu +target_triplet = x86_64-unknown-linux-gnu +subdir = include +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_$(V)) +am__v_P_ = $(am__v_P_$(AM_DEFAULT_VERBOSITY)) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing aclocal-1.14 +AMTAR = $${TAR-tar} +AM_DEFAULT_VERBOSITY = 1 +AR = ar +AUTOCONF = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoconf +AUTOHEADER = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoheader +AUTOMAKE = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing automake-1.14 +AWK = gawk +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -g -O2 -Wall +CPP = gcc -E +CPPFLAGS = +CYGPATH_W = echo +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +DLLTOOL = false +DSYMUTIL = +DUMPBIN = +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = /bin/grep -E +EXEEXT = +FGREP = /bin/grep -F +FUSE_MODULE_CFLAGS = +FUSE_MODULE_LIBS = +GNUTLS_CFLAGS = +GNUTLS_LIBS = +GREP = /bin/grep +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = $(install_sh) -c -s +LD = /usr/bin/ld -m elf_x86_64 +LDCONFIG = /sbin/ldconfig +LDFLAGS = +LIBFUSE_LITE_CFLAGS = +LIBFUSE_LITE_LIBS = -lpthread +LIBGCRYPT_CFLAGS = +LIBGCRYPT_CONFIG = +LIBGCRYPT_LIBS = +LIBNTFS_3G_VERSION = 86 +LIBNTFS_CPPFLAGS = +LIBNTFS_LIBS = +LIBOBJS = +LIBS = +LIBTOOL = $(SHELL) $(top_builddir)/libtool +LIPO = +LN_S = ln -s +LTLIBOBJS = +MAINT = # +MAKEINFO = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing makeinfo +MANIFEST_TOOL = : +MKDIR_P = /bin/mkdir -p +MKNTFS_CPPFLAGS = +MKNTFS_LIBS = +MV = /bin/mv +NM = /usr/bin/nm -B +NMEDIT = +NTFSPROGS_STATIC_LIBS = +OBJDUMP = objdump +OBJEXT = o +OTOOL = +OTOOL64 = +OUTPUT_FORMAT = +PACKAGE = ntfs-3g +PACKAGE_BUGREPORT = ntfs-3g-devel@lists.sf.net +PACKAGE_NAME = ntfs-3g +PACKAGE_STRING = ntfs-3g 2015.3.14 +PACKAGE_TARNAME = ntfs-3g +PACKAGE_URL = +PACKAGE_VERSION = 2015.3.14 +PATH_SEPARATOR = : +PKG_CONFIG = /usr/bin/pkg-config +PKG_CONFIG_LIBDIR = +PKG_CONFIG_PATH = +RANLIB = ranlib +RM = /bin/rm +SED = /bin/sed +SET_MAKE = +SHELL = /bin/bash +STRIP = strip +VERSION = 2015.3.14 +abs_builddir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/include +abs_srcdir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/include +abs_top_builddir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14 +abs_top_srcdir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14 +ac_ct_AR = ar +ac_ct_CC = gcc +ac_ct_DUMPBIN = +all_includes = +all_libraries = +am__include = include +am__leading_dot = . +am__quote = +am__tar = $${TAR-tar} chof - "$$tardir" +am__untar = $${TAR-tar} xf - +bindir = ${exec_prefix}/bin +build = x86_64-unknown-linux-gnu +build_alias = +build_cpu = x86_64 +build_os = linux-gnu +build_vendor = unknown +builddir = . +datadir = ${datarootdir} +datarootdir = ${prefix}/share +docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} +dvidir = ${docdir} +exec_prefix = ${prefix} +host = x86_64-unknown-linux-gnu +host_alias = +host_cpu = x86_64 +host_os = linux-gnu +host_vendor = unknown +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = $(MKDIR_P) +ntfs3gincludedir = $(includedir)/ntfs-3g +oldincludedir = /usr/include +pdfdir = ${docdir} +pkgconfigdir = $(libdir)/pkgconfig +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +rootbindir = /bin +rootlibdir = /lib +rootsbindir = /sbin +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +srcdir = . +sysconfdir = ${prefix}/etc +target = x86_64-unknown-linux-gnu +target_alias = +target_cpu = x86_64 +target_os = linux-gnu +target_vendor = unknown +top_build_prefix = ../ +top_builddir = .. +top_srcdir = .. +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +SUBDIRS = ntfs-3g fuse-lite +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: # $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: # $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): # $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100755 index 0000000000000000000000000000000000000000..0fa17025850685b5d693287117832fb003ea622b --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,4 @@ + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in + +SUBDIRS = ntfs-3g fuse-lite diff --git a/include/Makefile.in b/include/Makefile.in new file mode 100755 index 0000000000000000000000000000000000000000..048d6aa5979588addef82e817ad7fabcfb0f4abb --- /dev/null +++ b/include/Makefile.in @@ -0,0 +1,639 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = include +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FUSE_MODULE_CFLAGS = @FUSE_MODULE_CFLAGS@ +FUSE_MODULE_LIBS = @FUSE_MODULE_LIBS@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDCONFIG = @LDCONFIG@ +LDFLAGS = @LDFLAGS@ +LIBFUSE_LITE_CFLAGS = @LIBFUSE_LITE_CFLAGS@ +LIBFUSE_LITE_LIBS = @LIBFUSE_LITE_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBNTFS_3G_VERSION = @LIBNTFS_3G_VERSION@ +LIBNTFS_CPPFLAGS = @LIBNTFS_CPPFLAGS@ +LIBNTFS_LIBS = @LIBNTFS_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MKNTFS_CPPFLAGS = @MKNTFS_CPPFLAGS@ +MKNTFS_LIBS = @MKNTFS_LIBS@ +MV = @MV@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NTFSPROGS_STATIC_LIBS = @NTFSPROGS_STATIC_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OUTPUT_FORMAT = @OUTPUT_FORMAT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +RM = @RM@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +all_includes = @all_includes@ +all_libraries = @all_libraries@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +ntfs3gincludedir = @ntfs3gincludedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rootbindir = @rootbindir@ +rootlibdir = @rootlibdir@ +rootsbindir = @rootsbindir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +SUBDIRS = ntfs-3g fuse-lite +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/include/fuse-lite/Makefile b/include/fuse-lite/Makefile new file mode 100755 index 0000000000000000000000000000000000000000..e84fa1e27d8c3831bb32ac52a7ab5f54c5fa6d64 --- /dev/null +++ b/include/fuse-lite/Makefile @@ -0,0 +1,534 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# include/fuse-lite/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + + +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/ntfs-3g +pkgincludedir = $(includedir)/ntfs-3g +pkglibdir = $(libdir)/ntfs-3g +pkglibexecdir = $(libexecdir)/ntfs-3g +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = x86_64-unknown-linux-gnu +host_triplet = x86_64-unknown-linux-gnu +target_triplet = x86_64-unknown-linux-gnu +subdir = include/fuse-lite +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(noinst_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_$(V)) +am__v_P_ = $(am__v_P_$(AM_DEFAULT_VERBOSITY)) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing aclocal-1.14 +AMTAR = $${TAR-tar} +AM_DEFAULT_VERBOSITY = 1 +AR = ar +AUTOCONF = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoconf +AUTOHEADER = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoheader +AUTOMAKE = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing automake-1.14 +AWK = gawk +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -g -O2 -Wall +CPP = gcc -E +CPPFLAGS = +CYGPATH_W = echo +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +DLLTOOL = false +DSYMUTIL = +DUMPBIN = +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = /bin/grep -E +EXEEXT = +FGREP = /bin/grep -F +FUSE_MODULE_CFLAGS = +FUSE_MODULE_LIBS = +GNUTLS_CFLAGS = +GNUTLS_LIBS = +GREP = /bin/grep +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = $(install_sh) -c -s +LD = /usr/bin/ld -m elf_x86_64 +LDCONFIG = /sbin/ldconfig +LDFLAGS = +LIBFUSE_LITE_CFLAGS = +LIBFUSE_LITE_LIBS = -lpthread +LIBGCRYPT_CFLAGS = +LIBGCRYPT_CONFIG = +LIBGCRYPT_LIBS = +LIBNTFS_3G_VERSION = 86 +LIBNTFS_CPPFLAGS = +LIBNTFS_LIBS = +LIBOBJS = +LIBS = +LIBTOOL = $(SHELL) $(top_builddir)/libtool +LIPO = +LN_S = ln -s +LTLIBOBJS = +MAINT = # +MAKEINFO = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing makeinfo +MANIFEST_TOOL = : +MKDIR_P = /bin/mkdir -p +MKNTFS_CPPFLAGS = +MKNTFS_LIBS = +MV = /bin/mv +NM = /usr/bin/nm -B +NMEDIT = +NTFSPROGS_STATIC_LIBS = +OBJDUMP = objdump +OBJEXT = o +OTOOL = +OTOOL64 = +OUTPUT_FORMAT = +PACKAGE = ntfs-3g +PACKAGE_BUGREPORT = ntfs-3g-devel@lists.sf.net +PACKAGE_NAME = ntfs-3g +PACKAGE_STRING = ntfs-3g 2015.3.14 +PACKAGE_TARNAME = ntfs-3g +PACKAGE_URL = +PACKAGE_VERSION = 2015.3.14 +PATH_SEPARATOR = : +PKG_CONFIG = /usr/bin/pkg-config +PKG_CONFIG_LIBDIR = +PKG_CONFIG_PATH = +RANLIB = ranlib +RM = /bin/rm +SED = /bin/sed +SET_MAKE = +SHELL = /bin/bash +STRIP = strip +VERSION = 2015.3.14 +abs_builddir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/include/fuse-lite +abs_srcdir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/include/fuse-lite +abs_top_builddir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14 +abs_top_srcdir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14 +ac_ct_AR = ar +ac_ct_CC = gcc +ac_ct_DUMPBIN = +all_includes = +all_libraries = +am__include = include +am__leading_dot = . +am__quote = +am__tar = $${TAR-tar} chof - "$$tardir" +am__untar = $${TAR-tar} xf - +bindir = ${exec_prefix}/bin +build = x86_64-unknown-linux-gnu +build_alias = +build_cpu = x86_64 +build_os = linux-gnu +build_vendor = unknown +builddir = . +datadir = ${datarootdir} +datarootdir = ${prefix}/share +docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} +dvidir = ${docdir} +exec_prefix = ${prefix} +host = x86_64-unknown-linux-gnu +host_alias = +host_cpu = x86_64 +host_os = linux-gnu +host_vendor = unknown +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = $(MKDIR_P) +ntfs3gincludedir = $(includedir)/ntfs-3g +oldincludedir = /usr/include +pdfdir = ${docdir} +pkgconfigdir = $(libdir)/pkgconfig +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +rootbindir = /bin +rootlibdir = /lib +rootsbindir = /sbin +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +srcdir = . +sysconfdir = ${prefix}/etc +target = x86_64-unknown-linux-gnu +target_alias = +target_cpu = x86_64 +target_os = linux-gnu +target_vendor = unknown +top_build_prefix = ../../ +top_builddir = ../.. +top_srcdir = ../.. +MAINTAINERCLEANFILES = Makefile.in +noinst_HEADERS = \ + fuse.h \ + fuse_common.h \ + fuse_lowlevel.h \ + fuse_lowlevel_compat.h \ + fuse_opt.h \ + fuse_kernel.h + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: # $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/fuse-lite/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/fuse-lite/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: # $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): # $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/include/fuse-lite/Makefile.am b/include/fuse-lite/Makefile.am new file mode 100755 index 0000000000000000000000000000000000000000..15a84bbf5d6f3af56c1c7f754e074a79cfe65593 --- /dev/null +++ b/include/fuse-lite/Makefile.am @@ -0,0 +1,10 @@ + +MAINTAINERCLEANFILES = Makefile.in + +noinst_HEADERS = \ + fuse.h \ + fuse_common.h \ + fuse_lowlevel.h \ + fuse_lowlevel_compat.h \ + fuse_opt.h \ + fuse_kernel.h diff --git a/include/fuse-lite/Makefile.in b/include/fuse-lite/Makefile.in new file mode 100755 index 0000000000000000000000000000000000000000..9bc340fb3caaab389da9d4e4bb19c635df9f5287 --- /dev/null +++ b/include/fuse-lite/Makefile.in @@ -0,0 +1,534 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = include/fuse-lite +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(noinst_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FUSE_MODULE_CFLAGS = @FUSE_MODULE_CFLAGS@ +FUSE_MODULE_LIBS = @FUSE_MODULE_LIBS@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDCONFIG = @LDCONFIG@ +LDFLAGS = @LDFLAGS@ +LIBFUSE_LITE_CFLAGS = @LIBFUSE_LITE_CFLAGS@ +LIBFUSE_LITE_LIBS = @LIBFUSE_LITE_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBNTFS_3G_VERSION = @LIBNTFS_3G_VERSION@ +LIBNTFS_CPPFLAGS = @LIBNTFS_CPPFLAGS@ +LIBNTFS_LIBS = @LIBNTFS_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MKNTFS_CPPFLAGS = @MKNTFS_CPPFLAGS@ +MKNTFS_LIBS = @MKNTFS_LIBS@ +MV = @MV@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NTFSPROGS_STATIC_LIBS = @NTFSPROGS_STATIC_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OUTPUT_FORMAT = @OUTPUT_FORMAT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +RM = @RM@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +all_includes = @all_includes@ +all_libraries = @all_libraries@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +ntfs3gincludedir = @ntfs3gincludedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rootbindir = @rootbindir@ +rootlibdir = @rootlibdir@ +rootsbindir = @rootsbindir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +MAINTAINERCLEANFILES = Makefile.in +noinst_HEADERS = \ + fuse.h \ + fuse_common.h \ + fuse_lowlevel.h \ + fuse_lowlevel_compat.h \ + fuse_opt.h \ + fuse_kernel.h + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/fuse-lite/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/fuse-lite/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/include/fuse-lite/fuse.h b/include/fuse-lite/fuse.h new file mode 100755 index 0000000000000000000000000000000000000000..2e3d53d85f792651d08f6bc498faf8941d3ddd62 --- /dev/null +++ b/include/fuse-lite/fuse.h @@ -0,0 +1,694 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#ifndef _FUSE_H_ +#define _FUSE_H_ + +/** @file + * + * This file defines the library interface of FUSE + */ + +#include "fuse_common.h" + +#include <fcntl.h> +#include <time.h> +#include <utime.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/statvfs.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------------------------------------- * + * Basic FUSE API * + * ----------------------------------------------------------- */ + +/** Handle for a FUSE filesystem */ +struct fuse; + +/** Structure containing a raw command */ +struct fuse_cmd; + +/** Function to add an entry in a readdir() operation + * + * @param buf the buffer passed to the readdir() operation + * @param name the file name of the directory entry + * @param stat file attributes, can be NULL + * @param off offset of the next entry or zero + * @return 1 if buffer is full, zero otherwise + */ +typedef int (*fuse_fill_dir_t) (void *buf, const char *name, + const struct stat *stbuf, off_t off); + +/** + * The file system operations: + * + * Most of these should work very similarly to the well known UNIX + * file system operations. A major exception is that instead of + * returning an error in 'errno', the operation should return the + * negated error value (-errno) directly. + * + * All methods are optional, but some are essential for a useful + * filesystem (e.g. getattr). Open, flush, release, fsync, opendir, + * releasedir, fsyncdir, access, create, ftruncate, fgetattr, lock, + * init and destroy are special purpose methods, without which a full + * featured filesystem can still be implemented. + * + * Almost all operations take a path which can be of any length. + * + * Changed in fuse 2.8.0 (regardless of API version) + * Previously, paths were limited to a length of PATH_MAX. + */ + +struct fuse_operations { + /** Get file attributes. + * + * Similar to stat(). The 'st_dev' and 'st_blksize' fields are + * ignored. The 'st_ino' field is ignored except if the 'use_ino' + * mount option is given. + */ + int (*getattr) (const char *, struct stat *); + + /** Read the target of a symbolic link + * + * The buffer should be filled with a null terminated string. The + * buffer size argument includes the space for the terminating + * null character. If the linkname is too long to fit in the + * buffer, it should be truncated. The return value should be 0 + * for success. + */ + int (*readlink) (const char *, char *, size_t); + + /** Create a file node + * + * This is called for creation of all non-directory, non-symlink + * nodes. If the filesystem defines a create() method, then for + * regular files that will be called instead. + */ + int (*mknod) (const char *, mode_t, dev_t); + + /** Create a directory + * + * Note that the mode argument may not have the type specification + * bits set, i.e. S_ISDIR(mode) can be false. To obtain the + * correct directory type bits use mode|S_IFDIR + * */ + int (*mkdir) (const char *, mode_t); + + /** Remove a file */ + int (*unlink) (const char *); + + /** Remove a directory */ + int (*rmdir) (const char *); + + /** Create a symbolic link */ + int (*symlink) (const char *, const char *); + + /** Rename a file */ + int (*rename) (const char *, const char *); + + /** Create a hard link to a file */ + int (*link) (const char *, const char *); + + /** Change the permission bits of a file */ + int (*chmod) (const char *, mode_t); + + /** Change the owner and group of a file */ + int (*chown) (const char *, uid_t, gid_t); + + /** Change the size of a file */ + int (*truncate) (const char *, off_t); + + /** Change the access and/or modification times of a file + * + * Deprecated, use utimens() instead. + */ + int (*utime) (const char *, struct utimbuf *); + + /** File open operation + * + * No creation (O_CREAT, O_EXCL) and by default also no + * truncation (O_TRUNC) flags will be passed to open(). If an + * application specifies O_TRUNC, fuse first calls truncate() + * and then open(). Only if 'atomic_o_trunc' has been + * specified and kernel version is 2.6.24 or later, O_TRUNC is + * passed on to open. + * + * Unless the 'default_permissions' mount option is given, + * open should check if the operation is permitted for the + * given flags. Optionally open may also return an arbitrary + * filehandle in the fuse_file_info structure, which will be + * passed to all file operations. + * + * Changed in version 2.2 + */ + int (*open) (const char *, struct fuse_file_info *); + + /** Read data from an open file + * + * Read should return exactly the number of bytes requested except + * on EOF or error, otherwise the rest of the data will be + * substituted with zeroes. An exception to this is when the + * 'direct_io' mount option is specified, in which case the return + * value of the read system call will reflect the return value of + * this operation. + * + * Changed in version 2.2 + */ + int (*read) (const char *, char *, size_t, off_t, + struct fuse_file_info *); + + /** Write data to an open file + * + * Write should return exactly the number of bytes requested + * except on error. An exception to this is when the 'direct_io' + * mount option is specified (see read operation). + * + * Changed in version 2.2 + */ + int (*write) (const char *, const char *, size_t, off_t, + struct fuse_file_info *); + + /** Get file system statistics + * + * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored + * + * Replaced 'struct statfs' parameter with 'struct statvfs' in + * version 2.5 + */ + int (*statfs) (const char *, struct statvfs *); + + /** Possibly flush cached data + * + * BIG NOTE: This is not equivalent to fsync(). It's not a + * request to sync dirty data. + * + * Flush is called on each close() of a file descriptor. So if a + * filesystem wants to return write errors in close() and the file + * has cached dirty data, this is a good place to write back data + * and return any errors. Since many applications ignore close() + * errors this is not always useful. + * + * NOTE: The flush() method may be called more than once for each + * open(). This happens if more than one file descriptor refers + * to an opened file due to dup(), dup2() or fork() calls. It is + * not possible to determine if a flush is final, so each flush + * should be treated equally. Multiple write-flush sequences are + * relatively rare, so this shouldn't be a problem. + * + * Filesystems shouldn't assume that flush will always be called + * after some writes, or that if will be called at all. + * + * Changed in version 2.2 + */ + int (*flush) (const char *, struct fuse_file_info *); + + /** Release an open file + * + * Release is called when there are no more references to an open + * file: all file descriptors are closed and all memory mappings + * are unmapped. + * + * For every open() call there will be exactly one release() call + * with the same flags and file descriptor. It is possible to + * have a file opened more than once, in which case only the last + * release will mean, that no more reads/writes will happen on the + * file. The return value of release is ignored. + * + * Changed in version 2.2 + */ + int (*release) (const char *, struct fuse_file_info *); + + /** Synchronize file contents + * + * If the datasync parameter is non-zero, then only the user data + * should be flushed, not the meta data. + * + * Changed in version 2.2 + */ + int (*fsync) (const char *, int, struct fuse_file_info *); + + /** Set extended attributes */ + int (*setxattr) (const char *, const char *, const char *, size_t, int); + + /** Get extended attributes */ + int (*getxattr) (const char *, const char *, char *, size_t); + + /** List extended attributes */ + int (*listxattr) (const char *, char *, size_t); + + /** Remove extended attributes */ + int (*removexattr) (const char *, const char *); + + /** Open directory + * + * This method should check if the open operation is permitted for + * this directory + * + * Introduced in version 2.3 + */ + int (*opendir) (const char *, struct fuse_file_info *); + + /** Read directory + * + * The filesystem may choose between two modes of operation: + * + * 1) The readdir implementation ignores the offset parameter, and + * passes zero to the filler function's offset. The filler + * function will not return '1' (unless an error happens), so the + * whole directory is read in a single readdir operation. + * + * 2) The readdir implementation keeps track of the offsets of the + * directory entries. It uses the offset parameter and always + * passes non-zero offset to the filler function. When the buffer + * is full (or an error happens) the filler function will return + * '1'. + * + * Introduced in version 2.3 + */ + int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t, + struct fuse_file_info *); + + /** Release directory + * + * Introduced in version 2.3 + */ + int (*releasedir) (const char *, struct fuse_file_info *); + + /** Synchronize directory contents + * + * If the datasync parameter is non-zero, then only the user data + * should be flushed, not the meta data + * + * Introduced in version 2.3 + */ + int (*fsyncdir) (const char *, int, struct fuse_file_info *); + + /** + * Initialize filesystem + * + * The return value will passed in the private_data field of + * fuse_context to all file operations and as a parameter to the + * destroy() method. + * + * Introduced in version 2.3 + * Changed in version 2.6 + */ + void *(*init) (struct fuse_conn_info *conn); + + /** + * Clean up filesystem + * + * Called on filesystem exit. + * + * Introduced in version 2.3 + */ + void (*destroy) (void *); + + /** + * Check file access permissions + * + * This will be called for the access() system call. If the + * 'default_permissions' mount option is given, this method is not + * called. + * + * This method is not called under Linux kernel versions 2.4.x + * + * Introduced in version 2.5 + */ + int (*access) (const char *, int); + + /** + * Create and open a file + * + * If the file does not exist, first create it with the specified + * mode, and then open it. + * + * If this method is not implemented or under Linux kernel + * versions earlier than 2.6.15, the mknod() and open() methods + * will be called instead. + * + * Introduced in version 2.5 + */ + int (*create) (const char *, mode_t, struct fuse_file_info *); + + /** + * Change the size of an open file + * + * This method is called instead of the truncate() method if the + * truncation was invoked from an ftruncate() system call. + * + * If this method is not implemented or under Linux kernel + * versions earlier than 2.6.15, the truncate() method will be + * called instead. + * + * Introduced in version 2.5 + */ + int (*ftruncate) (const char *, off_t, struct fuse_file_info *); + + /** + * Get attributes from an open file + * + * This method is called instead of the getattr() method if the + * file information is available. + * + * Currently this is only called after the create() method if that + * is implemented (see above). Later it may be called for + * invocations of fstat() too. + * + * Introduced in version 2.5 + */ + int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *); + + /** + * Perform POSIX file locking operation + * + * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. + * + * For the meaning of fields in 'struct flock' see the man page + * for fcntl(2). The l_whence field will always be set to + * SEEK_SET. + * + * For checking lock ownership, the 'fuse_file_info->owner' + * argument must be used. + * + * For F_GETLK operation, the library will first check currently + * held locks, and if a conflicting lock is found it will return + * information without calling this method. This ensures, that + * for local locks the l_pid field is correctly filled in. The + * results may not be accurate in case of race conditions and in + * the presence of hard links, but it's unlikly that an + * application would rely on accurate GETLK results in these + * cases. If a conflicting lock is not found, this method will be + * called, and the filesystem may fill out l_pid by a meaningful + * value, or it may leave this field zero. + * + * For F_SETLK and F_SETLKW the l_pid field will be set to the pid + * of the process performing the locking operation. + * + * Note: if this method is not implemented, the kernel will still + * allow file locking to work locally. Hence it is only + * interesting for network filesystems and similar. + * + * Introduced in version 2.6 + */ + int (*lock) (const char *, struct fuse_file_info *, int cmd, + struct flock *); + + /** + * Change the access and modification times of a file with + * nanosecond resolution + * + * Introduced in version 2.6 + */ + int (*utimens) (const char *, const struct timespec tv[2]); + + /** + * Map block index within file to block index within device + * + * Note: This makes sense only for block device backed filesystems + * mounted with the 'blkdev' option + * + * Introduced in version 2.6 + */ + int (*bmap) (const char *, size_t blocksize, uint64_t *idx); + + /** + * Ioctl + * + * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in + * 64bit environment. The size and direction of data is + * determined by _IOC_*() decoding of cmd. For _IOC_NONE, + * data will be NULL, for _IOC_WRITE data is out area, for + * _IOC_READ in area and if both are set in/out area. In all + * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes. + * + * Introduced in version 2.8 + */ + int (*ioctl) (const char *, int cmd, void *arg, + struct fuse_file_info *, unsigned int flags, void *data); + + /* + * The flags below have been discarded, they should not be used + */ + unsigned int flag_nullpath_ok : 1; + /** + * Reserved flags, don't set + */ + unsigned int flag_reserved : 30; + +}; + +/** Extra context that may be needed by some filesystems + * + * The uid, gid and pid fields are not filled in case of a writepage + * operation. + */ +struct fuse_context { + /** Pointer to the fuse object */ + struct fuse *fuse; + + /** User ID of the calling process */ + uid_t uid; + + /** Group ID of the calling process */ + gid_t gid; + + /** Thread ID of the calling process */ + pid_t pid; + + /** Private filesystem data */ + void *private_data; + + /** Umask of the calling process (introduced in version 2.8) */ + mode_t umask; +}; + +/* ----------------------------------------------------------- * + * More detailed API * + * ----------------------------------------------------------- */ + +/** + * Create a new FUSE filesystem. + * + * @param ch the communication channel + * @param args argument vector + * @param op the filesystem operations + * @param op_size the size of the fuse_operations structure + * @param user_data user data supplied in the context during the init() method + * @return the created FUSE handle + */ +struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, + const struct fuse_operations *op, size_t op_size, + void *user_data); + +/** + * Destroy the FUSE handle. + * + * The communication channel attached to the handle is also destroyed. + * + * NOTE: This function does not unmount the filesystem. If this is + * needed, call fuse_unmount() before calling this function. + * + * @param f the FUSE handle + */ +void fuse_destroy(struct fuse *f); + +/** + * FUSE event loop. + * + * Requests from the kernel are processed, and the appropriate + * operations are called. + * + * @param f the FUSE handle + * @return 0 if no error occurred, -1 otherwise + */ +int fuse_loop(struct fuse *f); + +/** + * Exit from event loop + * + * @param f the FUSE handle + */ +void fuse_exit(struct fuse *f); + +/** + * Get the current context + * + * The context is only valid for the duration of a filesystem + * operation, and thus must not be stored and used later. + * + * @return the context + */ +struct fuse_context *fuse_get_context(void); + +/** + * Check if a request has already been interrupted + * + * @param req request handle + * @return 1 if the request has been interrupted, 0 otherwise + */ +int fuse_interrupted(void); + +/* + * Stacking API + */ + +/** + * Fuse filesystem object + * + * This is opaque object represents a filesystem layer + */ +struct fuse_fs; + +/* + * These functions call the relevant filesystem operation, and return + * the result. + * + * If the operation is not defined, they return -ENOSYS, with the + * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir, + * fuse_fs_releasedir and fuse_fs_statfs, which return 0. + */ + +int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf); +int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf, + struct fuse_file_info *fi); +int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, + const char *newpath); +int fuse_fs_unlink(struct fuse_fs *fs, const char *path); +int fuse_fs_rmdir(struct fuse_fs *fs, const char *path); +int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, + const char *path); +int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath); +int fuse_fs_release(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_open(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, + off_t off, struct fuse_file_info *fi); +int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi); +int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi); +int fuse_fs_flush(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf); +int fuse_fs_opendir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, + fuse_fill_dir_t filler, off_t off, + struct fuse_file_info *fi); +int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi); +int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi); +int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, + struct fuse_file_info *fi); +int fuse_fs_lock(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi, int cmd, struct flock *lock); +int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode); +int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid); +int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size); +int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off_t size, + struct fuse_file_info *fi); +int fuse_fs_utimens(struct fuse_fs *fs, const char *path, + const struct timespec tv[2]); +int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask); +int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, + size_t len); +int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, + dev_t rdev); +int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode); +int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, + const char *value, size_t size, int flags); +int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, + char *value, size_t size); +int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, + size_t size); +int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, + const char *name); +int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, + uint64_t *idx); +int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg, + struct fuse_file_info *fi, unsigned int flags, void *data); +void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn); +void fuse_fs_destroy(struct fuse_fs *fs); + +/** + * Create a new fuse filesystem object + * + * This is usually called from the factory of a fuse module to create + * a new instance of a filesystem. + * + * @param op the filesystem operations + * @param op_size the size of the fuse_operations structure + * @param user_data user data supplied in the context during the init() method + * @return a new filesystem object + */ +struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, + void *user_data); + +#ifdef __SOLARIS__ + +/** + * Filesystem module + * + * Filesystem modules are registered with the FUSE_REGISTER_MODULE() + * macro. + * + * If the "-omodules=modname:..." option is present, filesystem + * objects are created and pushed onto the stack with the 'factory' + * function. + */ +struct fuse_module { + /** + * Name of filesystem + */ + const char *name; + + /** + * Factory for creating filesystem objects + * + * The function may use and remove options from 'args' that belong + * to this module. + * + * For now the 'fs' vector always contains exactly one filesystem. + * This is the filesystem which will be below the newly created + * filesystem in the stack. + * + * @param args the command line arguments + * @param fs NULL terminated filesystem object vector + * @return the new filesystem object + */ + struct fuse_fs *(*factory)(struct fuse_args *args, struct fuse_fs *fs[]); + + struct fuse_module *next; + struct fusemod_so *so; + int ctr; +}; + +#endif /* __SOLARIS__ */ + +/* ----------------------------------------------------------- * + * Advanced API for event handling, don't worry about this... * + * ----------------------------------------------------------- */ + +/* NOTE: the following functions are deprecated, and will be removed + from the 3.0 API. Use the lowlevel session functions instead */ + +/** Get session from fuse object */ +struct fuse_session *fuse_get_session(struct fuse *f); + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_H_ */ diff --git a/include/fuse-lite/fuse_common.h b/include/fuse-lite/fuse_common.h new file mode 100755 index 0000000000000000000000000000000000000000..8a3f1df3bd6cb3a065fd70f661104c92dd0726f3 --- /dev/null +++ b/include/fuse-lite/fuse_common.h @@ -0,0 +1,244 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +/** @file */ + +#if !defined(_FUSE_H_) && !defined(_FUSE_LOWLEVEL_H_) +#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead." +#endif + +#ifndef _FUSE_COMMON_H_ +#define _FUSE_COMMON_H_ + +#include "fuse_opt.h" +#include <stdio.h> /* temporary */ +#include <stdint.h> + +/** Major version of FUSE library interface */ +#define FUSE_MAJOR_VERSION 2 + +/** Minor version of FUSE library interface */ +#ifdef POSIXACLS +#define FUSE_MINOR_VERSION 8 +#else +#define FUSE_MINOR_VERSION 7 +#endif + +#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) +#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) + +/* This interface uses 64 bit off_t */ +#if defined(__SOLARIS__) && !defined(__x86_64__) && (_FILE_OFFSET_BITS != 64) +#error Please add -D_FILE_OFFSET_BITS=64 to your compile flags! +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef POSIXACLS +/* + * FUSE_CAP_DONT_MASK: don't apply umask to file mode on create operations + */ +#define FUSE_CAP_DONT_MASK (1 << 6) +#endif + +#define FUSE_CAP_BIG_WRITES (1 << 5) +#define FUSE_CAP_IOCTL_DIR (1 << 11) + +/** + * Ioctl flags + * + * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine + * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed + * FUSE_IOCTL_RETRY: retry with new iovecs + * FUSE_IOCTL_DIR: is a directory + */ +#define FUSE_IOCTL_COMPAT (1 << 0) +#define FUSE_IOCTL_UNRESTRICTED (1 << 1) +#define FUSE_IOCTL_RETRY (1 << 2) +#define FUSE_IOCTL_DIR (1 << 4) + +#define FUSE_IOCTL_MAX_IOV 256 + +/** + * Information about open files + * + * Changed in version 2.5 + */ +struct fuse_file_info { + /** Open flags. Available in open() and release() */ + int flags; + + /** Old file handle, don't use */ + unsigned long fh_old; + + /** In case of a write operation indicates if this was caused by a + writepage */ + int writepage; + + /** Can be filled in by open, to use direct I/O on this file. + Introduced in version 2.4 */ + unsigned int direct_io : 1; + + /** Can be filled in by open, to indicate, that cached file data + need not be invalidated. Introduced in version 2.4 */ + unsigned int keep_cache : 1; + + /** Indicates a flush operation. Set in flush operation, also + maybe set in highlevel lock operation and lowlevel release + operation. Introduced in version 2.6 */ + unsigned int flush : 1; + + /** Padding. Do not use*/ + unsigned int padding : 29; + + /** File handle. May be filled in by filesystem in open(). + Available in all other file operations */ + uint64_t fh; + + /** Lock owner id. Available in locking operations and flush */ + uint64_t lock_owner; +}; + +/** + * Connection information, passed to the ->init() method + * + * Some of the elements are read-write, these can be changed to + * indicate the value requested by the filesystem. The requested + * value must usually be smaller than the indicated value. + */ +struct fuse_conn_info { + /** + * Major version of the protocol (read-only) + */ + unsigned proto_major; + + /** + * Minor version of the protocol (read-only) + */ + unsigned proto_minor; + + /** + * Is asynchronous read supported (read-write) + */ + unsigned async_read; + + /** + * Maximum size of the write buffer + */ + unsigned max_write; + + /** + * Maximum readahead + */ + unsigned max_readahead; + + unsigned capable; + unsigned want; + /** + * For future use. + */ + unsigned reserved[25]; + }; + +struct fuse_session; +struct fuse_chan; + +/** + * Create a FUSE mountpoint + * + * Returns a control file descriptor suitable for passing to + * fuse_new() + * + * @param mountpoint the mount point path + * @param args argument vector + * @return the communication channel on success, NULL on failure + */ +struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args); + +/** + * Umount a FUSE mountpoint + * + * @param mountpoint the mount point path + * @param ch the communication channel + */ +void fuse_unmount(const char *mountpoint, struct fuse_chan *ch); + +#ifdef __SOLARIS__ +/** + * Parse common options + * + * The following options are parsed: + * + * '-f' foreground + * '-d' '-odebug' foreground, but keep the debug option + * '-s' single threaded + * '-h' '--help' help + * '-ho' help without header + * '-ofsname=..' file system name, if not present, then set to the program + * name + * + * All parameters may be NULL + * + * @param args argument vector + * @param mountpoint the returned mountpoint, should be freed after use + * @param multithreaded set to 1 unless the '-s' option is present + * @param foreground set to 1 if one of the relevant options is present + * @return 0 on success, -1 on failure + */ +int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, + int *multithreaded, int *foreground); + +/** + * Go into the background + * + * @param foreground if true, stay in the foreground + * @return 0 on success, -1 on failure + */ +int fuse_daemonize(int foreground); + +#endif /* __SOLARIS__ */ + +/** + * Get the version of the library + * + * @return the version + */ +int fuse_version(void); + +/* ----------------------------------------------------------- * + * Signal handling * + * ----------------------------------------------------------- */ + +/** + * Exit session on HUP, TERM and INT signals and ignore PIPE signal + * + * Stores session in a global variable. May only be called once per + * process until fuse_remove_signal_handlers() is called. + * + * @param se the session to exit + * @return 0 on success, -1 on failure + */ +int fuse_set_signal_handlers(struct fuse_session *se); + +/** + * Restore default signal handlers + * + * Resets global session. After this fuse_set_signal_handlers() may + * be called again. + * + * @param se the same session as given in fuse_set_signal_handlers() + */ +void fuse_remove_signal_handlers(struct fuse_session *se); + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_COMMON_H_ */ diff --git a/include/fuse-lite/fuse_kernel.h b/include/fuse-lite/fuse_kernel.h new file mode 100755 index 0000000000000000000000000000000000000000..c031b04f7ffa5cce83f42ae6793137d5a6c83957 --- /dev/null +++ b/include/fuse-lite/fuse_kernel.h @@ -0,0 +1,439 @@ +/* + This file defines the kernel interface of FUSE + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + + This -- and only this -- header file may also be distributed under + the terms of the BSD Licence as follows: + + Copyright (C) 2001-2007 Miklos Szeredi. 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. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + * + * 7.12 + * - add umask flag to input argument of open, mknod and mkdir +*/ + +#ifndef linux +#include <sys/types.h> +#define __u64 uint64_t +#define __u32 uint32_t +#define __s32 int32_t +#else +#include <asm/types.h> +#include <linux/major.h> +#endif + +/** Version number of this interface */ +#define FUSE_KERNEL_VERSION 7 + +/** Minor version number of this interface + * We introduce ourself as 7.18 (Posix ACLS : 7.12, IOCTL_DIR : 7.18) + * and we expect features features defined for 7.18, but not implemented + * here to not be triggered by ntfs-3g. + */ +#define FUSE_KERNEL_MINOR_VERSION 18 + +/* + * For binary compatibility with old kernels we accept falling back + * to 7.12 or earlier maximum version supported by the kernel + */ + +#define FUSE_KERNEL_MAJOR_FALLBACK 7 +#define FUSE_KERNEL_MINOR_FALLBACK 12 + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/** The major number of the fuse character device */ +#define FUSE_MAJOR MISC_MAJOR + +/** The minor number of the fuse character device */ +#define FUSE_MINOR 229 + +/* Make sure all structures are padded to 64bit boundary, so 32bit + userspace works under 64bit kernels */ + +struct fuse_attr { + __u64 ino; + __u64 size; + __u64 blocks; + __u64 atime; + __u64 mtime; + __u64 ctime; + __u32 atimensec; + __u32 mtimensec; + __u32 ctimensec; + __u32 mode; + __u32 nlink; + __u32 uid; + __u32 gid; + __u32 rdev; + __u64 filling; /* JPA needed for minor >= 12, but meaning unknown */ +}; + +struct fuse_kstatfs { + __u64 blocks; + __u64 bfree; + __u64 bavail; + __u64 files; + __u64 ffree; + __u32 bsize; + __u32 namelen; + __u32 frsize; + __u32 padding; + __u32 spare[6]; +}; + +struct fuse_file_lock { + __u64 start; + __u64 end; + __u32 type; + __u32 pid; /* tgid */ +}; + +/** + * Bitmasks for fuse_setattr_in.valid + */ +#define FATTR_MODE (1 << 0) +#define FATTR_UID (1 << 1) +#define FATTR_GID (1 << 2) +#define FATTR_SIZE (1 << 3) +#define FATTR_ATIME (1 << 4) +#define FATTR_MTIME (1 << 5) +#define FATTR_FH (1 << 6) + +/** + * Flags returned by the OPEN request + * + * FOPEN_DIRECT_IO: bypass page cache for this open file + * FOPEN_KEEP_CACHE: don't invalidate the data cache on open + */ +#define FOPEN_DIRECT_IO (1 << 0) +#define FOPEN_KEEP_CACHE (1 << 1) + +/** + * INIT request/reply flags + * FUSE_BIG_WRITES: allow big writes to be issued to the file system + * FUSE_DONT_MASK: don't apply umask to file mode on create operations + * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories + */ +#define FUSE_ASYNC_READ (1 << 0) +#define FUSE_POSIX_LOCKS (1 << 1) +#define FUSE_BIG_WRITES (1 << 5) +#define FUSE_DONT_MASK (1 << 6) +#define FUSE_HAS_IOCTL_DIR (1 << 11) + +/** + * Release flags + */ +#define FUSE_RELEASE_FLUSH (1 << 0) + +enum fuse_opcode { + FUSE_LOOKUP = 1, + FUSE_FORGET = 2, /* no reply */ + FUSE_GETATTR = 3, + FUSE_SETATTR = 4, + FUSE_READLINK = 5, + FUSE_SYMLINK = 6, + FUSE_MKNOD = 8, + FUSE_MKDIR = 9, + FUSE_UNLINK = 10, + FUSE_RMDIR = 11, + FUSE_RENAME = 12, + FUSE_LINK = 13, + FUSE_OPEN = 14, + FUSE_READ = 15, + FUSE_WRITE = 16, + FUSE_STATFS = 17, + FUSE_RELEASE = 18, + FUSE_FSYNC = 20, + FUSE_SETXATTR = 21, + FUSE_GETXATTR = 22, + FUSE_LISTXATTR = 23, + FUSE_REMOVEXATTR = 24, + FUSE_FLUSH = 25, + FUSE_INIT = 26, + FUSE_OPENDIR = 27, + FUSE_READDIR = 28, + FUSE_RELEASEDIR = 29, + FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, + FUSE_ACCESS = 34, + FUSE_CREATE = 35, + FUSE_INTERRUPT = 36, + FUSE_BMAP = 37, + FUSE_DESTROY = 38, + FUSE_IOCTL = 39, +}; + +/* The read buffer is required to be at least 8k, but may be much larger */ +#define FUSE_MIN_READ_BUFFER 8192 +#define FUSE_COMPAT_ENTRY_OUT_SIZE 120 /* JPA */ + +struct fuse_entry_out { + __u64 nodeid; /* Inode ID */ + __u64 generation; /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + __u64 entry_valid; /* Cache timeout for the name */ + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 entry_valid_nsec; + __u32 attr_valid_nsec; + struct fuse_attr attr; +}; + +struct fuse_forget_in { + __u64 nlookup; +}; + +#define FUSE_COMPAT_FUSE_ATTR_OUT_SIZE 96 /* JPA */ + +struct fuse_attr_out { + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 attr_valid_nsec; + __u32 dummy; + struct fuse_attr attr; +}; + +#define FUSE_COMPAT_MKNOD_IN_SIZE 8 + +struct fuse_mknod_in { + __u32 mode; + __u32 rdev; + __u32 umask; + __u32 padding; +}; + +struct fuse_mkdir_in { + __u32 mode; + __u32 umask; +}; + +struct fuse_rename_in { + __u64 newdir; +}; + +struct fuse_link_in { + __u64 oldnodeid; +}; + +struct fuse_setattr_in { + __u32 valid; + __u32 padding; + __u64 fh; + __u64 size; + __u64 unused1; + __u64 atime; + __u64 mtime; + __u64 unused2; + __u32 atimensec; + __u32 mtimensec; + __u32 unused3; + __u32 mode; + __u32 unused4; + __u32 uid; + __u32 gid; + __u32 unused5; +}; + +struct fuse_open_in { + __u32 flags; + __u32 mode; /* unused for protocol < 7.12 */ +}; + +struct fuse_create_in { + __u32 flags; + __u32 mode; + __u32 umask; + __u32 padding; +}; + +struct fuse_open_out { + __u64 fh; + __u32 open_flags; + __u32 padding; +}; + +struct fuse_release_in { + __u64 fh; + __u32 flags; + __u32 release_flags; + __u64 lock_owner; +}; + +struct fuse_flush_in { + __u64 fh; + __u32 unused; + __u32 padding; + __u64 lock_owner; +}; + +struct fuse_read_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 padding; +}; + +#define FUSE_COMPAT_WRITE_IN_SIZE 24 /* JPA */ + +struct fuse_write_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 write_flags; + __u64 lock_owner; /* JPA */ + __u32 flags; /* JPA */ + __u32 padding; /* JPA */ +}; + +struct fuse_write_out { + __u32 size; + __u32 padding; +}; + +#define FUSE_COMPAT_STATFS_SIZE 48 + +struct fuse_statfs_out { + struct fuse_kstatfs st; +}; + +struct fuse_fsync_in { + __u64 fh; + __u32 fsync_flags; + __u32 padding; +}; + +struct fuse_setxattr_in { + __u32 size; + __u32 flags; +}; + +struct fuse_getxattr_in { + __u32 size; + __u32 padding; +}; + +struct fuse_getxattr_out { + __u32 size; + __u32 padding; +}; + +struct fuse_lk_in { + __u64 fh; + __u64 owner; + struct fuse_file_lock lk; +}; + +struct fuse_lk_out { + struct fuse_file_lock lk; +}; + +struct fuse_access_in { + __u32 mask; + __u32 padding; +}; + +struct fuse_init_in { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; +}; + +struct fuse_init_out { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; + __u32 unused; + __u32 max_write; +}; + +struct fuse_interrupt_in { + __u64 unique; +}; + +struct fuse_bmap_in { + __u64 block; + __u32 blocksize; + __u32 padding; +}; + +struct fuse_bmap_out { + __u64 block; +}; + +struct fuse_ioctl_in { + __u64 fh; + __u32 flags; + __u32 cmd; + __u64 arg; + __u32 in_size; + __u32 out_size; +}; + +struct fuse_ioctl_iovec { + __u64 base; + __u64 len; +}; + +struct fuse_ioctl_out { + __s32 result; + __u32 flags; + __u32 in_iovs; + __u32 out_iovs; +}; + +struct fuse_in_header { + __u32 len; + __u32 opcode; + __u64 unique; + __u64 nodeid; + __u32 uid; + __u32 gid; + __u32 pid; + __u32 padding; +}; + +struct fuse_out_header { + __u32 len; + __s32 error; + __u64 unique; +}; + +struct fuse_dirent { + __u64 ino; + __u64 off; + __u32 namelen; + __u32 type; + char name[0]; +}; + +#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) +#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1)) +#define FUSE_DIRENT_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) diff --git a/include/fuse-lite/fuse_lowlevel.h b/include/fuse-lite/fuse_lowlevel.h new file mode 100755 index 0000000000000000000000000000000000000000..8c84d9656030bc5a273d5410bfcb4c2581f8888d --- /dev/null +++ b/include/fuse-lite/fuse_lowlevel.h @@ -0,0 +1,1414 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#ifndef _FUSE_LOWLEVEL_H_ +#define _FUSE_LOWLEVEL_H_ + +/** @file + * + * Low level API + */ + +#include "fuse_common.h" + +#include <utime.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/statvfs.h> +#include <sys/uio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------------------------------------- * + * Miscellaneous definitions * + * ----------------------------------------------------------- */ + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/** Inode number type */ +typedef unsigned long fuse_ino_t; + +/** Request pointer type */ +typedef struct fuse_req *fuse_req_t; + +/** + * Session + * + * This provides hooks for processing requests, and exiting + */ +struct fuse_session; + +/** + * Channel + * + * A communication channel, providing hooks for sending and receiving + * messages + */ +struct fuse_chan; + +/** Directory entry parameters supplied to fuse_reply_entry() */ +struct fuse_entry_param { + /** Unique inode number + * + * In lookup, zero means negative entry (from version 2.5) + * Returning ENOENT also means negative entry, but by setting zero + * ino the kernel may cache negative entries for entry_timeout + * seconds. + */ + fuse_ino_t ino; + + /** Generation number for this entry. + * + * The ino/generation pair should be unique for the filesystem's + * lifetime. It must be non-zero, otherwise FUSE will treat it as an + * error. + */ + unsigned long generation; + + /** Inode attributes. + * + * Even if attr_timeout == 0, attr must be correct. For example, + * for open(), FUSE uses attr.st_size from lookup() to determine + * how many bytes to request. If this value is not correct, + * incorrect data will be returned. + */ + struct stat attr; + + /** Validity timeout (in seconds) for the attributes */ + double attr_timeout; + + /** Validity timeout (in seconds) for the name */ + double entry_timeout; +}; + +/** Additional context associated with requests */ +struct fuse_ctx { + /** User ID of the calling process */ + uid_t uid; + + /** Group ID of the calling process */ + gid_t gid; + + /** Thread ID of the calling process */ + pid_t pid; + + /** Umask of the calling process (introduced in version 2.8) */ + mode_t umask; +}; + +/* 'to_set' flags in setattr */ +#define FUSE_SET_ATTR_MODE (1 << 0) +#define FUSE_SET_ATTR_UID (1 << 1) +#define FUSE_SET_ATTR_GID (1 << 2) +#define FUSE_SET_ATTR_SIZE (1 << 3) +#define FUSE_SET_ATTR_ATIME (1 << 4) +#define FUSE_SET_ATTR_MTIME (1 << 5) +#define FUSE_SET_ATTR_ATIME_NOW (1 << 7) +#define FUSE_SET_ATTR_MTIME_NOW (1 << 8) + +/* ----------------------------------------------------------- * + * Request methods and replies * + * ----------------------------------------------------------- */ + +/** + * Low level filesystem operations + * + * Most of the methods (with the exception of init and destroy) + * receive a request handle (fuse_req_t) as their first argument. + * This handle must be passed to one of the specified reply functions. + * + * This may be done inside the method invocation, or after the call + * has returned. The request handle is valid until one of the reply + * functions is called. + * + * Other pointer arguments (name, fuse_file_info, etc) are not valid + * after the call has returned, so if they are needed later, their + * contents have to be copied. + * + * The filesystem sometimes needs to handle a return value of -ENOENT + * from the reply function, which means, that the request was + * interrupted, and the reply discarded. For example if + * fuse_reply_open() return -ENOENT means, that the release method for + * this file will not be called. + */ +struct fuse_lowlevel_ops { + /** + * Initialize filesystem + * + * Called before any other filesystem method + * + * There's no reply to this function + * + * @param userdata the user data passed to fuse_lowlevel_new() + */ + void (*init) (void *userdata, struct fuse_conn_info *conn); + + /** + * Clean up filesystem + * + * Called on filesystem exit + * + * There's no reply to this function + * + * @param userdata the user data passed to fuse_lowlevel_new() + */ + void (*destroy) (void *userdata); + + /** + * Look up a directory entry by name and get its attributes. + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name the name to look up + */ + void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); + + /** + * Forget about an inode + * + * The nlookup parameter indicates the number of lookups + * previously performed on this inode. + * + * If the filesystem implements inode lifetimes, it is recommended + * that inodes acquire a single reference on each lookup, and lose + * nlookup references on each forget. + * + * The filesystem may ignore forget calls, if the inodes don't + * need to have a limited lifetime. + * + * On unmount it is not guaranteed, that all referenced inodes + * will receive a forget message. + * + * Valid replies: + * fuse_reply_none + * + * @param req request handle + * @param ino the inode number + * @param nlookup the number of lookups to forget + */ + void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup); + + /** + * Get file attributes + * + * Valid replies: + * fuse_reply_attr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi for future use, currently always NULL + */ + void (*getattr) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); + + /** + * Set file attributes + * + * In the 'attr' argument only members indicated by the 'to_set' + * bitmask contain valid values. Other members contain undefined + * values. + * + * If the setattr was invoked from the ftruncate() system call + * under Linux kernel versions 2.6.15 or later, the fi->fh will + * contain the value set by the open method or will be undefined + * if the open method didn't set any value. Otherwise (not + * ftruncate call, or kernel version earlier than 2.6.15) the fi + * parameter will be NULL. + * + * Valid replies: + * fuse_reply_attr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param attr the attributes + * @param to_set bit mask of attributes which should be set + * @param fi file information, or NULL + * + * Changed in version 2.5: + * file information filled in for ftruncate + */ + void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int to_set, struct fuse_file_info *fi); + + /** + * Read symbolic link + * + * Valid replies: + * fuse_reply_readlink + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + */ + void (*readlink) (fuse_req_t req, fuse_ino_t ino); + + /** + * Create file node + * + * Create a regular file, character device, block device, fifo or + * socket node. + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode file type and mode with which to create the new file + * @param rdev the device number (only valid if created file is a device) + */ + void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, dev_t rdev); + + /** + * Create a directory + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode with which to create the new file + */ + void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode); + + /** + * Remove a file + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to remove + */ + void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); + + /** + * Remove a directory + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to remove + */ + void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); + + /** + * Create a symbolic link + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param link the contents of the symbolic link + * @param parent inode number of the parent directory + * @param name to create + */ + void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent, + const char *name); + + /** Rename a file + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the old parent directory + * @param name old name + * @param newparent inode number of the new parent directory + * @param newname new name + */ + void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, + fuse_ino_t newparent, const char *newname); + + /** + * Create a hard link + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param ino the old inode number + * @param newparent inode number of the new parent directory + * @param newname new name to create + */ + void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, + const char *newname); + + /** + * Open a file + * + * Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and + * O_TRUNC) are available in fi->flags. + * + * Filesystem may store an arbitrary file handle (pointer, index, + * etc) in fi->fh, and use this in other all other file operations + * (read, write, flush, release, fsync). + * + * Filesystem may also implement stateless file I/O and not store + * anything in fi->fh. + * + * There are also some flags (direct_io, keep_cache) which the + * filesystem may set in fi, to change the way the file is opened. + * See fuse_file_info structure in <fuse_common.h> for more details. + * + * Valid replies: + * fuse_reply_open + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*open) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); + + /** + * Read data + * + * Read should send exactly the number of bytes requested except + * on EOF or error, otherwise the rest of the data will be + * substituted with zeroes. An exception to this is when the file + * has been opened in 'direct_io' mode, in which case the return + * value of the read system call will reflect the return value of + * this operation. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size number of bytes to read + * @param off offset to read from + * @param fi file information + */ + void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi); + + /** + * Write data + * + * Write should return exactly the number of bytes requested + * except on error. An exception to this is when the file has + * been opened in 'direct_io' mode, in which case the return value + * of the write system call will reflect the return value of this + * operation. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * Valid replies: + * fuse_reply_write + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param buf data to write + * @param size number of bytes to write + * @param off offset to write to + * @param fi file information + */ + void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi); + + /** + * Flush method + * + * This is called on each close() of the opened file. + * + * Since file descriptors can be duplicated (dup, dup2, fork), for + * one open call there may be many flush calls. + * + * Filesystems shouldn't assume that flush will always be called + * after some writes, or that if will be called at all. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * NOTE: the name of the method is misleading, since (unlike + * fsync) the filesystem is not forced to flush pending writes. + * One reason to flush data, is if the filesystem wants to return + * write errors. + * + * If the filesystem supports file locking operations (setlk, + * getlk) it should remove all locks belonging to 'fi->owner'. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*flush) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); + + /** + * Release an open file + * + * Release is called when there are no more references to an open + * file: all file descriptors are closed and all memory mappings + * are unmapped. + * + * For every open call there will be exactly one release call. + * + * The filesystem may reply with an error, but error values are + * not returned to close() or munmap() which triggered the + * release. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * fi->flags will contain the same flags as for open. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*release) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); + + /** + * Synchronize file contents + * + * If the datasync parameter is non-zero, then only the user data + * should be flushed, not the meta data. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param datasync flag indicating if only data should be flushed + * @param fi file information + */ + void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi); + + /** + * Open a directory + * + * Filesystem may store an arbitrary file handle (pointer, index, + * etc) in fi->fh, and use this in other all other directory + * stream operations (readdir, releasedir, fsyncdir). + * + * Filesystem may also implement stateless directory I/O and not + * store anything in fi->fh, though that makes it impossible to + * implement standard conforming directory stream operations in + * case the contents of the directory can change between opendir + * and releasedir. + * + * Valid replies: + * fuse_reply_open + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*opendir) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); + + /** + * Read directory + * + * Send a buffer filled using fuse_add_direntry(), with size not + * exceeding the requested size. Send an empty buffer on end of + * stream. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size maximum number of bytes to send + * @param off offset to continue reading the directory stream + * @param fi file information + */ + void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi); + + /** + * Release an open directory + * + * For every opendir call there will be exactly one releasedir + * call. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*releasedir) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); + + /** + * Synchronize directory contents + * + * If the datasync parameter is non-zero, then only the directory + * contents should be flushed, not the meta data. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param datasync flag indicating if only data should be flushed + * @param fi file information + */ + void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi); + + /** + * Get file system statistics + * + * Valid replies: + * fuse_reply_statfs + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number, zero means "undefined" + */ + void (*statfs) (fuse_req_t req, fuse_ino_t ino); + + /** + * Set an extended attribute + * + * Valid replies: + * fuse_reply_err + */ + void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags); + + /** + * Get an extended attribute + * + * If size is zero, the size of the value should be sent with + * fuse_reply_xattr. + * + * If the size is non-zero, and the value fits in the buffer, the + * value should be sent with fuse_reply_buf. + * + * If the size is too small for the value, the ERANGE error should + * be sent. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_xattr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param name of the extended attribute + * @param size maximum size of the value to send + */ + void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size); + + /** + * List extended attribute names + * + * If size is zero, the total size of the attribute list should be + * sent with fuse_reply_xattr. + * + * If the size is non-zero, and the null character separated + * attribute list fits in the buffer, the list should be sent with + * fuse_reply_buf. + * + * If the size is too small for the list, the ERANGE error should + * be sent. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_xattr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size maximum size of the list to send + */ + void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size); + + /** + * Remove an extended attribute + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param name of the extended attribute + */ + void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name); + + /** + * Check file access permissions + * + * This will be called for the access() system call. If the + * 'default_permissions' mount option is given, this method is not + * called. + * + * This method is not called under Linux kernel versions 2.4.x + * + * Introduced in version 2.5 + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param mask requested access mode + */ + void (*access) (fuse_req_t req, fuse_ino_t ino, int mask); + + /** + * Create and open a file + * + * If the file does not exist, first create it with the specified + * mode, and then open it. + * + * Open flags (with the exception of O_NOCTTY) are available in + * fi->flags. + * + * Filesystem may store an arbitrary file handle (pointer, index, + * etc) in fi->fh, and use this in other all other file operations + * (read, write, flush, release, fsync). + * + * There are also some flags (direct_io, keep_cache) which the + * filesystem may set in fi, to change the way the file is opened. + * See fuse_file_info structure in <fuse_common.h> for more details. + * + * If this method is not implemented or under Linux kernel + * versions earlier than 2.6.15, the mknod() and open() methods + * will be called instead. + * + * Introduced in version 2.5 + * + * Valid replies: + * fuse_reply_create + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode file type and mode with which to create the new file + * @param fi file information + */ + void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, struct fuse_file_info *fi); + + /** + * Test for a POSIX file lock + * + * Introduced in version 2.6 + * + * Valid replies: + * fuse_reply_lock + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param lock the region/type to test + */ + void (*getlk) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock); + + /** + * Acquire, modify or release a POSIX file lock + * + * For POSIX threads (NPTL) there's a 1-1 relation between pid and + * owner, but otherwise this is not always the case. For checking + * lock ownership, 'fi->owner' must be used. The l_pid field in + * 'struct flock' should only be used to fill in this field in + * getlk(). + * + * Note: if the locking methods are not implemented, the kernel + * will still allow file locking to work locally. Hence these are + * only interesting for network filesystems and similar. + * + * Introduced in version 2.6 + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param lock the region/type to test + * @param sleep locking operation may sleep + */ + void (*setlk) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, + struct flock *lock, int sleep); + + /** + * Map block index within file to block index within device + * + * Note: This makes sense only for block device backed filesystems + * mounted with the 'blkdev' option + * + * Introduced in version 2.6 + * + * Valid replies: + * fuse_reply_bmap + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param blocksize unit of block index + * @param idx block index within file + */ + void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize, + uint64_t idx); + /** + * Ioctl + * + * Note: For unrestricted ioctls (not allowed for FUSE + * servers), data in and out areas can be discovered by giving + * iovs and setting FUSE_IOCTL_RETRY in @flags. For + * restricted ioctls, kernel prepares in/out data area + * according to the information encoded in cmd. + * + * Introduced in version 2.8 + * + * Valid replies: + * fuse_reply_ioctl_retry + * fuse_reply_ioctl + * fuse_reply_ioctl_iov + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param cmd ioctl command + * @param arg ioctl argument + * @param fi file information + * @param flags for FUSE_IOCTL_* flags + * @param in_buf data fetched from the caller + * @param in_bufsz number of fetched bytes + * @param out_bufsz maximum size of output data + */ + void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, + struct fuse_file_info *fi, unsigned flags, + const void *in_buf, size_t in_bufsz, size_t out_bufsz); + +}; + +/** + * Reply with an error code or success + * + * Possible requests: + * all except forget + * + * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, + * removexattr and setlk may send a zero code + * + * @param req request handle + * @param err the positive error value, or zero for success + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_err(fuse_req_t req, int err); + +/** + * Don't send reply + * + * Possible requests: + * forget + * + * @param req request handle + */ +void fuse_reply_none(fuse_req_t req); + +/** + * Reply with a directory entry + * + * Possible requests: + * lookup, mknod, mkdir, symlink, link + * + * @param req request handle + * @param e the entry parameters + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e); + +/** + * Reply with a directory entry and open parameters + * + * currently the following members of 'fi' are used: + * fh, direct_io, keep_cache + * + * Possible requests: + * create + * + * @param req request handle + * @param e the entry parameters + * @param fi file information + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, + const struct fuse_file_info *fi); + +/** + * Reply with attributes + * + * Possible requests: + * getattr, setattr + * + * @param req request handle + * @param the attributes + * @param attr_timeout validity timeout (in seconds) for the attributes + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_attr(fuse_req_t req, const struct stat *attr, + double attr_timeout); + +/** + * Reply with the contents of a symbolic link + * + * Possible requests: + * readlink + * + * @param req request handle + * @param link symbolic link contents + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_readlink(fuse_req_t req, const char *link); + +/** + * Reply with open parameters + * + * currently the following members of 'fi' are used: + * fh, direct_io, keep_cache + * + * Possible requests: + * open, opendir + * + * @param req request handle + * @param fi file information + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi); + +/** + * Reply with number of bytes written + * + * Possible requests: + * write + * + * @param req request handle + * @param count the number of bytes written + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_write(fuse_req_t req, size_t count); + +/** + * Reply with data + * + * Possible requests: + * read, readdir, getxattr, listxattr + * + * @param req request handle + * @param buf buffer containing data + * @param size the size of data in bytes + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); + +#ifdef POSIXACLS +/** + * Reply with data vector + * + * Possible requests: + * read, readdir, getxattr, listxattr + * + * @param req request handle + * @param iov the vector containing the data + * @param count the size of vector + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count); +#endif + +/** + * Reply with filesystem statistics + * + * Possible requests: + * statfs + * + * @param req request handle + * @param stbuf filesystem statistics + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf); + +/** + * Reply with needed buffer size + * + * Possible requests: + * getxattr, listxattr + * + * @param req request handle + * @param count the buffer size needed in bytes + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_xattr(fuse_req_t req, size_t count); + +/** + * Reply with file lock information + * + * Possible requests: + * getlk + * + * @param req request handle + * @param lock the lock information + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_lock(fuse_req_t req, struct flock *lock); + +/** + * Reply with block index + * + * Possible requests: + * bmap + * + * @param req request handle + * @param idx block index within device + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_bmap(fuse_req_t req, uint64_t idx); + +/* ----------------------------------------------------------- * + * Filling a buffer in readdir * + * ----------------------------------------------------------- */ + +/** + * Add a directory entry to the buffer + * + * Buffer needs to be large enough to hold the entry. Of it's not, + * then the entry is not filled in but the size of the entry is still + * returned. The caller can check this by comparing the bufsize + * parameter with the returned entry size. If the entry size is + * larger than the buffer size, the operation failed. + * + * From the 'stbuf' argument the st_ino field and bits 12-15 of the + * st_mode field are used. The other fields are ignored. + * + * Note: offsets do not necessarily represent physical offsets, and + * could be any marker, that enables the implementation to find a + * specific point in the directory stream. + * + * @param req request handle + * @param buf the point where the new entry will be added to the buffer + * @param bufsize remaining size of the buffer + * @param the name of the entry + * @param stbuf the file attributes + * @param off the offset of the next entry + * @return the space needed for the entry + */ +size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, + const char *name, const struct stat *stbuf, + off_t off); + +/** + * Reply to finish ioctl + * + * Possible requests: + * ioctl + * + * @param req request handle + * @param result result to be passed to the caller + * @param buf buffer containing output data + * @param size length of output data + */ +int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size); + + +/* ----------------------------------------------------------- * + * Utility functions * + * ----------------------------------------------------------- */ + +/** + * Get the userdata from the request + * + * @param req request handle + * @return the user data passed to fuse_lowlevel_new() + */ +void *fuse_req_userdata(fuse_req_t req); + +/** + * Get the context from the request + * + * The pointer returned by this function will only be valid for the + * request's lifetime + * + * @param req request handle + * @return the context structure + */ +const struct fuse_ctx *fuse_req_ctx(fuse_req_t req); + +/** + * Callback function for an interrupt + * + * @param req interrupted request + * @param data user data + */ +typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data); + +/** + * Register/unregister callback for an interrupt + * + * If an interrupt has already happened, then the callback function is + * called from within this function, hence it's not possible for + * interrupts to be lost. + * + * @param req request handle + * @param func the callback function or NULL for unregister + * @parm data user data passed to the callback function + */ +void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, + void *data); + +/** + * Check if a request has already been interrupted + * + * @param req request handle + * @return 1 if the request has been interrupted, 0 otherwise + */ +int fuse_req_interrupted(fuse_req_t req); + +/* ----------------------------------------------------------- * + * Filesystem setup * + * ----------------------------------------------------------- */ + +#ifdef __SOLARIS__ + +/* Deprecated, don't use */ +int fuse_lowlevel_is_lib_option(const char *opt); + +#endif /* __SOLARIS__ */ + +/** + * Create a low level session + * + * @param args argument vector + * @param op the low level filesystem operations + * @param op_size sizeof(struct fuse_lowlevel_ops) + * @param userdata user data + * @return the created session object, or NULL on failure + */ +struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, + const struct fuse_lowlevel_ops *op, + size_t op_size, void *userdata); + +/* ----------------------------------------------------------- * + * Session interface * + * ----------------------------------------------------------- */ + +/** + * Session operations + * + * This is used in session creation + */ +struct fuse_session_ops { + /** + * Hook to process a request (mandatory) + * + * @param data user data passed to fuse_session_new() + * @param buf buffer containing the raw request + * @param len request length + * @param ch channel on which the request was received + */ + void (*process) (void *data, const char *buf, size_t len, + struct fuse_chan *ch); + + /** + * Hook for session exit and reset (optional) + * + * @param data user data passed to fuse_session_new() + * @param val exited status (1 - exited, 0 - not exited) + */ + void (*exit) (void *data, int val); + + /** + * Hook for querying the current exited status (optional) + * + * @param data user data passed to fuse_session_new() + * @return 1 if exited, 0 if not exited + */ + int (*exited) (void *data); + + /** + * Hook for cleaning up the channel on destroy (optional) + * + * @param data user data passed to fuse_session_new() + */ + void (*destroy) (void *data); +}; + +/** + * Create a new session + * + * @param op session operations + * @param data user data + * @return new session object, or NULL on failure + */ +struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data); + +/** + * Assign a channel to a session + * + * Note: currently only a single channel may be assigned. This may + * change in the future + * + * If a session is destroyed, the assigned channel is also destroyed + * + * @param se the session + * @param ch the channel + */ +void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch); + +/** + * Remove a channel from a session + * + * If the channel is not assigned to a session, then this is a no-op + * + * @param ch the channel to remove + */ +void fuse_session_remove_chan(struct fuse_chan *ch); + +/** + * Iterate over the channels assigned to a session + * + * The iterating function needs to start with a NULL channel, and + * after that needs to pass the previously returned channel to the + * function. + * + * @param se the session + * @param ch the previous channel, or NULL + * @return the next channel, or NULL if no more channels exist + */ +struct fuse_chan *fuse_session_next_chan(struct fuse_session *se, + struct fuse_chan *ch); + +/** + * Process a raw request + * + * @param se the session + * @param buf buffer containing the raw request + * @param len request length + * @param ch channel on which the request was received + */ +void fuse_session_process(struct fuse_session *se, const char *buf, size_t len, + struct fuse_chan *ch); + +/** + * Destroy a session + * + * @param se the session + */ +void fuse_session_destroy(struct fuse_session *se); + +/** + * Exit a session + * + * @param se the session + */ +void fuse_session_exit(struct fuse_session *se); + +/** + * Reset the exited status of a session + * + * @param se the session + */ +void fuse_session_reset(struct fuse_session *se); + +/** + * Query the exited status of a session + * + * @param se the session + * @return 1 if exited, 0 if not exited + */ +int fuse_session_exited(struct fuse_session *se); + +/** + * Enter a single threaded event loop + * + * @param se the session + * @return 0 on success, -1 on error + */ +int fuse_session_loop(struct fuse_session *se); + +/** + * Enter a multi-threaded event loop + * + * @param se the session + * @return 0 on success, -1 on error + */ +int fuse_session_loop_mt(struct fuse_session *se); + +/* ----------------------------------------------------------- * + * Channel interface * + * ----------------------------------------------------------- */ + +/** + * Channel operations + * + * This is used in channel creation + */ +struct fuse_chan_ops { + /** + * Hook for receiving a raw request + * + * @param ch pointer to the channel + * @param buf the buffer to store the request in + * @param size the size of the buffer + * @return the actual size of the raw request, or -1 on error + */ + int (*receive)(struct fuse_chan **chp, char *buf, size_t size); + + /** + * Hook for sending a raw reply + * + * A return value of -ENOENT means, that the request was + * interrupted, and the reply was discarded + * + * @param ch the channel + * @param iov vector of blocks + * @param count the number of blocks in vector + * @return zero on success, -errno on failure + */ + int (*send)(struct fuse_chan *ch, const struct iovec iov[], + size_t count); + + /** + * Destroy the channel + * + * @param ch the channel + */ + void (*destroy)(struct fuse_chan *ch); +}; + +/** + * Create a new channel + * + * @param op channel operations + * @param fd file descriptor of the channel + * @param bufsize the minimal receive buffer size + * @param data user data + * @return the new channel object, or NULL on failure + */ +struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd, + size_t bufsize, void *data); + +/** + * Query the file descriptor of the channel + * + * @param ch the channel + * @return the file descriptor passed to fuse_chan_new() + */ +int fuse_chan_fd(struct fuse_chan *ch); + +/** + * Query the minimal receive buffer size + * + * @param ch the channel + * @return the buffer size passed to fuse_chan_new() + */ +size_t fuse_chan_bufsize(struct fuse_chan *ch); + +/** + * Query the user data + * + * @param ch the channel + * @return the user data passed to fuse_chan_new() + */ +void *fuse_chan_data(struct fuse_chan *ch); + +/** + * Query the session to which this channel is assigned + * + * @param ch the channel + * @return the session, or NULL if the channel is not assigned + */ +struct fuse_session *fuse_chan_session(struct fuse_chan *ch); + +/** + * Receive a raw request + * + * A return value of -ENODEV means, that the filesystem was unmounted + * + * @param ch pointer to the channel + * @param buf the buffer to store the request in + * @param size the size of the buffer + * @return the actual size of the raw request, or -errno on error + */ +int fuse_chan_recv(struct fuse_chan **ch, char *buf, size_t size); + +/** + * Send a raw reply + * + * A return value of -ENOENT means, that the request was + * interrupted, and the reply was discarded + * + * @param ch the channel + * @param iov vector of blocks + * @param count the number of blocks in vector + * @return zero on success, -errno on failure + */ +int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], + size_t count); + +/** + * Destroy a channel + * + * @param ch the channel + */ +void fuse_chan_destroy(struct fuse_chan *ch); + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_LOWLEVEL_H_ */ diff --git a/include/fuse-lite/fuse_lowlevel_compat.h b/include/fuse-lite/fuse_lowlevel_compat.h new file mode 100755 index 0000000000000000000000000000000000000000..753d46bc9097e47e61e5d83d8c7e3457bffb1459 --- /dev/null +++ b/include/fuse-lite/fuse_lowlevel_compat.h @@ -0,0 +1,16 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +/* these definitions provide source compatibility to prior versions. + Do not include this file directly! */ + +size_t fuse_dirent_size(size_t namelen); + +char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf, + off_t off); + diff --git a/include/fuse-lite/fuse_opt.h b/include/fuse-lite/fuse_opt.h new file mode 100755 index 0000000000000000000000000000000000000000..7ae08af647416daddaa17c454f612cd764e7a710 --- /dev/null +++ b/include/fuse-lite/fuse_opt.h @@ -0,0 +1,261 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#ifndef _FUSE_OPT_H_ +#define _FUSE_OPT_H_ + +/** @file + * + * This file defines the option parsing interface of FUSE + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Option description + * + * This structure describes a single option, and and action associated + * with it, in case it matches. + * + * More than one such match may occur, in which case the action for + * each match is executed. + * + * There are three possible actions in case of a match: + * + * i) An integer (int or unsigned) variable determined by 'offset' is + * set to 'value' + * + * ii) The processing function is called, with 'value' as the key + * + * iii) An integer (any) or string (char *) variable determined by + * 'offset' is set to the value of an option parameter + * + * 'offset' should normally be either set to + * + * - 'offsetof(struct foo, member)' actions i) and iii) + * + * - -1 action ii) + * + * The 'offsetof()' macro is defined in the <stddef.h> header. + * + * The template determines which options match, and also have an + * effect on the action. Normally the action is either i) or ii), but + * if a format is present in the template, then action iii) is + * performed. + * + * The types of templates are: + * + * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only + * themselves. Invalid values are "--" and anything beginning + * with "-o" + * + * 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or + * the relevant option in a comma separated option list + * + * 3) "bar=", "--foo=", etc. These are variations of 1) and 2) + * which have a parameter + * + * 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform + * action iii). + * + * 5) "-x ", etc. Matches either "-xparam" or "-x param" as + * two separate arguments + * + * 6) "-x %s", etc. Combination of 4) and 5) + * + * If the format is "%s", memory is allocated for the string unlike + * with scanf(). + */ +struct fuse_opt { + /** Matching template and optional parameter formatting */ + const char *templ; + + /** + * Offset of variable within 'data' parameter of fuse_opt_parse() + * or -1 + */ + unsigned long offset; + + /** + * Value to set the variable to, or to be passed as 'key' to the + * processing function. Ignored if template has a format + */ + int value; +}; + +/** + * Key option. In case of a match, the processing function will be + * called with the specified key. + */ +#define FUSE_OPT_KEY(templ, key) { templ, -1U, key } + +/** + * Last option. An array of 'struct fuse_opt' must end with a NULL + * template value + */ +#define FUSE_OPT_END { .templ = NULL } + +/** + * Argument list + */ +struct fuse_args { + /** Argument count */ + int argc; + + /** Argument vector. NULL terminated */ + char **argv; + + /** Is 'argv' allocated? */ + int allocated; +}; + +/** + * Initializer for 'struct fuse_args' + */ +#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } + +/** + * Key value passed to the processing function if an option did not + * match any template + */ +#define FUSE_OPT_KEY_OPT -1 + +/** + * Key value passed to the processing function for all non-options + * + * Non-options are the arguments beginning with a charater other than + * '-' or all arguments after the special '--' option + */ +#define FUSE_OPT_KEY_NONOPT -2 + +/** + * Special key value for options to keep + * + * Argument is not passed to processing function, but behave as if the + * processing function returned 1 + */ +#define FUSE_OPT_KEY_KEEP -3 + +/** + * Special key value for options to discard + * + * Argument is not passed to processing function, but behave as if the + * processing function returned zero + */ +#define FUSE_OPT_KEY_DISCARD -4 + +/** + * Processing function + * + * This function is called if + * - option did not match any 'struct fuse_opt' + * - argument is a non-option + * - option did match and offset was set to -1 + * + * The 'arg' parameter will always contain the whole argument or + * option including the parameter if exists. A two-argument option + * ("-x foo") is always converted to single arguemnt option of the + * form "-xfoo" before this function is called. + * + * Options of the form '-ofoo' are passed to this function without the + * '-o' prefix. + * + * The return value of this function determines whether this argument + * is to be inserted into the output argument vector, or discarded. + * + * @param data is the user data passed to the fuse_opt_parse() function + * @param arg is the whole argument or option + * @param key determines why the processing function was called + * @param outargs the current output argument list + * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept + */ +typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, + struct fuse_args *outargs); + +/** + * Option parsing function + * + * If 'args' was returned from a previous call to fuse_opt_parse() or + * it was constructed from + * + * A NULL 'args' is equivalent to an empty argument vector + * + * A NULL 'opts' is equivalent to an 'opts' array containing a single + * end marker + * + * A NULL 'proc' is equivalent to a processing function always + * returning '1' + * + * @param args is the input and output argument list + * @param data is the user data + * @param opts is the option description array + * @param proc is the processing function + * @return -1 on error, 0 on success + */ +int fuse_opt_parse(struct fuse_args *args, void *data, + const struct fuse_opt opts[], fuse_opt_proc_t proc); + +/** + * Add an option to a comma separated option list + * + * @param opts is a pointer to an option list, may point to a NULL value + * @param opt is the option to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_add_opt(char **opts, const char *opt); + +/** + * Add an argument to a NULL terminated argument vector + * + * @param args is the structure containing the current argument list + * @param arg is the new argument to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_add_arg(struct fuse_args *args, const char *arg); + +/** + * Add an argument at the specified position in a NULL terminated + * argument vector + * + * Adds the argument to the N-th position. This is useful for adding + * options at the beggining of the array which must not come after the + * special '--' option. + * + * @param args is the structure containing the current argument list + * @param pos is the position at which to add the argument + * @param arg is the new argument to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg); + +/** + * Free the contents of argument list + * + * The structure itself is not freed + * + * @param args is the structure containing the argument list + */ +void fuse_opt_free_args(struct fuse_args *args); + + +/** + * Check if an option matches + * + * @param opts is the option description array + * @param opt is the option to match + * @return 1 if a match is found, 0 if not + */ +int fuse_opt_match(const struct fuse_opt opts[], const char *opt); + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_OPT_H_ */ diff --git a/include/ntfs-3g/Makefile b/include/ntfs-3g/Makefile new file mode 100755 index 0000000000000000000000000000000000000000..e526b297da62fe2ecd002ce0799a3577decddf7e --- /dev/null +++ b/include/ntfs-3g/Makefile @@ -0,0 +1,635 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# include/ntfs-3g/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + + +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/ntfs-3g +pkgincludedir = $(includedir)/ntfs-3g +pkglibdir = $(libdir)/ntfs-3g +pkglibexecdir = $(libexecdir)/ntfs-3g +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = x86_64-unknown-linux-gnu +host_triplet = x86_64-unknown-linux-gnu +target_triplet = x86_64-unknown-linux-gnu +subdir = include/ntfs-3g +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(am__noinst_HEADERS_DIST) $(am__ntfs3ginclude_HEADERS_DIST) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_$(V)) +am__v_P_ = $(am__v_P_$(AM_DEFAULT_VERBOSITY)) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__noinst_HEADERS_DIST = acls.h attrib.h attrlist.h bitmap.h \ + bootsect.h cache.h collate.h compat.h compress.h debug.h \ + device.h device_io.h dir.h ea.h efs.h endians.h index.h \ + inode.h ioctl.h layout.h lcnalloc.h logfile.h logging.h mft.h \ + misc.h mst.h ntfstime.h object_id.h param.h realpath.h \ + reparse.h runlist.h security.h support.h types.h unistr.h \ + volume.h xattrs.h +am__ntfs3ginclude_HEADERS_DIST = acls.h attrib.h attrlist.h bitmap.h \ + bootsect.h cache.h collate.h compat.h compress.h debug.h \ + device.h device_io.h dir.h ea.h efs.h endians.h index.h \ + inode.h ioctl.h layout.h lcnalloc.h logfile.h logging.h mft.h \ + misc.h mst.h ntfstime.h object_id.h param.h realpath.h \ + reparse.h runlist.h security.h support.h types.h unistr.h \ + volume.h xattrs.h +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(ntfs3gincludedir)" +HEADERS = $(noinst_HEADERS) $(ntfs3ginclude_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing aclocal-1.14 +AMTAR = $${TAR-tar} +AM_DEFAULT_VERBOSITY = 1 +AR = ar +AUTOCONF = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoconf +AUTOHEADER = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoheader +AUTOMAKE = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing automake-1.14 +AWK = gawk +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -g -O2 -Wall +CPP = gcc -E +CPPFLAGS = +CYGPATH_W = echo +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +DLLTOOL = false +DSYMUTIL = +DUMPBIN = +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = /bin/grep -E +EXEEXT = +FGREP = /bin/grep -F +FUSE_MODULE_CFLAGS = +FUSE_MODULE_LIBS = +GNUTLS_CFLAGS = +GNUTLS_LIBS = +GREP = /bin/grep +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = $(install_sh) -c -s +LD = /usr/bin/ld -m elf_x86_64 +LDCONFIG = /sbin/ldconfig +LDFLAGS = +LIBFUSE_LITE_CFLAGS = +LIBFUSE_LITE_LIBS = -lpthread +LIBGCRYPT_CFLAGS = +LIBGCRYPT_CONFIG = +LIBGCRYPT_LIBS = +LIBNTFS_3G_VERSION = 86 +LIBNTFS_CPPFLAGS = +LIBNTFS_LIBS = +LIBOBJS = +LIBS = +LIBTOOL = $(SHELL) $(top_builddir)/libtool +LIPO = +LN_S = ln -s +LTLIBOBJS = +MAINT = # +MAKEINFO = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing makeinfo +MANIFEST_TOOL = : +MKDIR_P = /bin/mkdir -p +MKNTFS_CPPFLAGS = +MKNTFS_LIBS = +MV = /bin/mv +NM = /usr/bin/nm -B +NMEDIT = +NTFSPROGS_STATIC_LIBS = +OBJDUMP = objdump +OBJEXT = o +OTOOL = +OTOOL64 = +OUTPUT_FORMAT = +PACKAGE = ntfs-3g +PACKAGE_BUGREPORT = ntfs-3g-devel@lists.sf.net +PACKAGE_NAME = ntfs-3g +PACKAGE_STRING = ntfs-3g 2015.3.14 +PACKAGE_TARNAME = ntfs-3g +PACKAGE_URL = +PACKAGE_VERSION = 2015.3.14 +PATH_SEPARATOR = : +PKG_CONFIG = /usr/bin/pkg-config +PKG_CONFIG_LIBDIR = +PKG_CONFIG_PATH = +RANLIB = ranlib +RM = /bin/rm +SED = /bin/sed +SET_MAKE = +SHELL = /bin/bash +STRIP = strip +VERSION = 2015.3.14 +abs_builddir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/include/ntfs-3g +abs_srcdir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/include/ntfs-3g +abs_top_builddir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14 +abs_top_srcdir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14 +ac_ct_AR = ar +ac_ct_CC = gcc +ac_ct_DUMPBIN = +all_includes = +all_libraries = +am__include = include +am__leading_dot = . +am__quote = +am__tar = $${TAR-tar} chof - "$$tardir" +am__untar = $${TAR-tar} xf - +bindir = ${exec_prefix}/bin +build = x86_64-unknown-linux-gnu +build_alias = +build_cpu = x86_64 +build_os = linux-gnu +build_vendor = unknown +builddir = . +datadir = ${datarootdir} +datarootdir = ${prefix}/share +docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} +dvidir = ${docdir} +exec_prefix = ${prefix} +host = x86_64-unknown-linux-gnu +host_alias = +host_cpu = x86_64 +host_os = linux-gnu +host_vendor = unknown +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = $(MKDIR_P) +ntfs3gincludedir = $(includedir)/ntfs-3g +oldincludedir = /usr/include +pdfdir = ${docdir} +pkgconfigdir = $(libdir)/pkgconfig +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +rootbindir = /bin +rootlibdir = /lib +rootsbindir = /sbin +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +srcdir = . +sysconfdir = ${prefix}/etc +target = x86_64-unknown-linux-gnu +target_alias = +target_cpu = x86_64 +target_os = linux-gnu +target_vendor = unknown +top_build_prefix = ../../ +top_builddir = ../.. +top_srcdir = ../.. +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +headers = \ + acls.h \ + attrib.h \ + attrlist.h \ + bitmap.h \ + bootsect.h \ + cache.h \ + collate.h \ + compat.h \ + compress.h \ + debug.h \ + device.h \ + device_io.h \ + dir.h \ + ea.h \ + efs.h \ + endians.h \ + index.h \ + inode.h \ + ioctl.h \ + layout.h \ + lcnalloc.h \ + logfile.h \ + logging.h \ + mft.h \ + misc.h \ + mst.h \ + ntfstime.h \ + object_id.h \ + param.h \ + realpath.h \ + reparse.h \ + runlist.h \ + security.h \ + support.h \ + types.h \ + unistr.h \ + volume.h \ + xattrs.h + +ntfs3ginclude_HEADERS = $(headers) +#noinst_HEADERS = $(headers) +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: # $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/ntfs-3g/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/ntfs-3g/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: # $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): # $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-ntfs3gincludeHEADERS: $(ntfs3ginclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(ntfs3ginclude_HEADERS)'; test -n "$(ntfs3gincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(ntfs3gincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(ntfs3gincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(ntfs3gincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(ntfs3gincludedir)" || exit $$?; \ + done + +uninstall-ntfs3gincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(ntfs3ginclude_HEADERS)'; test -n "$(ntfs3gincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(ntfs3gincludedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(ntfs3gincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-ntfs3gincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-ntfs3gincludeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man \ + install-ntfs3gincludeHEADERS install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-ntfs3gincludeHEADERS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/include/ntfs-3g/Makefile.am b/include/ntfs-3g/Makefile.am new file mode 100755 index 0000000000000000000000000000000000000000..11761bcbfd731d8cc2b6ddbb330b5717ef51c4ff --- /dev/null +++ b/include/ntfs-3g/Makefile.am @@ -0,0 +1,49 @@ + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in + +headers = \ + acls.h \ + attrib.h \ + attrlist.h \ + bitmap.h \ + bootsect.h \ + cache.h \ + collate.h \ + compat.h \ + compress.h \ + debug.h \ + device.h \ + device_io.h \ + dir.h \ + ea.h \ + efs.h \ + endians.h \ + index.h \ + inode.h \ + ioctl.h \ + layout.h \ + lcnalloc.h \ + logfile.h \ + logging.h \ + mft.h \ + misc.h \ + mst.h \ + ntfstime.h \ + object_id.h \ + param.h \ + realpath.h \ + reparse.h \ + runlist.h \ + security.h \ + support.h \ + types.h \ + unistr.h \ + volume.h \ + xattrs.h + +if INSTALL_LIBRARY +ntfs3ginclude_HEADERS = $(headers) +else +noinst_HEADERS = $(headers) +endif + diff --git a/include/ntfs-3g/Makefile.in b/include/ntfs-3g/Makefile.in new file mode 100755 index 0000000000000000000000000000000000000000..13a5bcda14d757b9aa7935647774425565616b8a --- /dev/null +++ b/include/ntfs-3g/Makefile.in @@ -0,0 +1,635 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = include/ntfs-3g +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(am__noinst_HEADERS_DIST) $(am__ntfs3ginclude_HEADERS_DIST) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__noinst_HEADERS_DIST = acls.h attrib.h attrlist.h bitmap.h \ + bootsect.h cache.h collate.h compat.h compress.h debug.h \ + device.h device_io.h dir.h ea.h efs.h endians.h index.h \ + inode.h ioctl.h layout.h lcnalloc.h logfile.h logging.h mft.h \ + misc.h mst.h ntfstime.h object_id.h param.h realpath.h \ + reparse.h runlist.h security.h support.h types.h unistr.h \ + volume.h xattrs.h +am__ntfs3ginclude_HEADERS_DIST = acls.h attrib.h attrlist.h bitmap.h \ + bootsect.h cache.h collate.h compat.h compress.h debug.h \ + device.h device_io.h dir.h ea.h efs.h endians.h index.h \ + inode.h ioctl.h layout.h lcnalloc.h logfile.h logging.h mft.h \ + misc.h mst.h ntfstime.h object_id.h param.h realpath.h \ + reparse.h runlist.h security.h support.h types.h unistr.h \ + volume.h xattrs.h +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(ntfs3gincludedir)" +HEADERS = $(noinst_HEADERS) $(ntfs3ginclude_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FUSE_MODULE_CFLAGS = @FUSE_MODULE_CFLAGS@ +FUSE_MODULE_LIBS = @FUSE_MODULE_LIBS@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDCONFIG = @LDCONFIG@ +LDFLAGS = @LDFLAGS@ +LIBFUSE_LITE_CFLAGS = @LIBFUSE_LITE_CFLAGS@ +LIBFUSE_LITE_LIBS = @LIBFUSE_LITE_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBNTFS_3G_VERSION = @LIBNTFS_3G_VERSION@ +LIBNTFS_CPPFLAGS = @LIBNTFS_CPPFLAGS@ +LIBNTFS_LIBS = @LIBNTFS_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MKNTFS_CPPFLAGS = @MKNTFS_CPPFLAGS@ +MKNTFS_LIBS = @MKNTFS_LIBS@ +MV = @MV@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NTFSPROGS_STATIC_LIBS = @NTFSPROGS_STATIC_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OUTPUT_FORMAT = @OUTPUT_FORMAT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +RM = @RM@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +all_includes = @all_includes@ +all_libraries = @all_libraries@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +ntfs3gincludedir = @ntfs3gincludedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rootbindir = @rootbindir@ +rootlibdir = @rootlibdir@ +rootsbindir = @rootsbindir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +headers = \ + acls.h \ + attrib.h \ + attrlist.h \ + bitmap.h \ + bootsect.h \ + cache.h \ + collate.h \ + compat.h \ + compress.h \ + debug.h \ + device.h \ + device_io.h \ + dir.h \ + ea.h \ + efs.h \ + endians.h \ + index.h \ + inode.h \ + ioctl.h \ + layout.h \ + lcnalloc.h \ + logfile.h \ + logging.h \ + mft.h \ + misc.h \ + mst.h \ + ntfstime.h \ + object_id.h \ + param.h \ + realpath.h \ + reparse.h \ + runlist.h \ + security.h \ + support.h \ + types.h \ + unistr.h \ + volume.h \ + xattrs.h + +@INSTALL_LIBRARY_TRUE@ntfs3ginclude_HEADERS = $(headers) +@INSTALL_LIBRARY_FALSE@noinst_HEADERS = $(headers) +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/ntfs-3g/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/ntfs-3g/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-ntfs3gincludeHEADERS: $(ntfs3ginclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(ntfs3ginclude_HEADERS)'; test -n "$(ntfs3gincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(ntfs3gincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(ntfs3gincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(ntfs3gincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(ntfs3gincludedir)" || exit $$?; \ + done + +uninstall-ntfs3gincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(ntfs3ginclude_HEADERS)'; test -n "$(ntfs3gincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(ntfs3gincludedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(ntfs3gincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-ntfs3gincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-ntfs3gincludeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man \ + install-ntfs3gincludeHEADERS install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-ntfs3gincludeHEADERS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/include/ntfs-3g/acls.h b/include/ntfs-3g/acls.h new file mode 100755 index 0000000000000000000000000000000000000000..38d2cd64a8587ea24885f84de8e94b3dd5b1ffe7 --- /dev/null +++ b/include/ntfs-3g/acls.h @@ -0,0 +1,203 @@ +/* + * + * Copyright (c) 2007-2008 Jean-Pierre Andre + * + */ + +/* + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ACLS_H +#define ACLS_H + +/* + * JPA configuration modes for security.c / acls.c + * should be moved to some config file + */ + +#define BUFSZ 1024 /* buffer size to read mapping file */ +#define MAPPINGFILE ".NTFS-3G/UserMapping" /* default mapping file */ +#define LINESZ 120 /* maximum useful size of a mapping line */ +#define CACHE_PERMISSIONS_BITS 6 /* log2 of unitary allocation of permissions */ +#define CACHE_PERMISSIONS_SIZE 262144 /* max cacheable permissions */ + +/* + * JPA The following must be in some library... + * but did not found out where + */ + +#define endian_rev16(x) (((x >> 8) & 255) | ((x & 255) << 8)) +#define endian_rev32(x) (((x >> 24) & 255) | ((x >> 8) & 0xff00) \ + | ((x & 0xff00) << 8) | ((x & 255) << 24)) + +#define cpu_to_be16(x) endian_rev16(cpu_to_le16(x)) +#define cpu_to_be32(x) endian_rev32(cpu_to_le32(x)) + +/* + * Macro definitions needed to share code with secaudit + */ + +#define NTFS_FIND_USID(map,uid,buf) ntfs_find_usid(map,uid,buf) +#define NTFS_FIND_GSID(map,gid,buf) ntfs_find_gsid(map,gid,buf) +#define NTFS_FIND_USER(map,usid) ntfs_find_user(map,usid) +#define NTFS_FIND_GROUP(map,gsid) ntfs_find_group(map,gsid) + + +/* + * Matching of ntfs permissions to Linux permissions + * these constants are adapted to endianness + * when setting, set them all + * when checking, check one is present + */ + + /* flags which are set to mean exec, write or read */ + +#define FILE_READ (FILE_READ_DATA) +#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) +#define FILE_EXEC (FILE_EXECUTE) +#define DIR_READ FILE_LIST_DIRECTORY +#define DIR_WRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD \ + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) +#define DIR_EXEC (FILE_TRAVERSE) + + /* flags tested for meaning exec, write or read */ + /* tests for write allow for interpretation of a sticky bit */ + +#define FILE_GREAD (FILE_READ_DATA | GENERIC_READ) +#define FILE_GWRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE) +#define FILE_GEXEC (FILE_EXECUTE | GENERIC_EXECUTE) +#define DIR_GREAD (FILE_LIST_DIRECTORY | GENERIC_READ) +#define DIR_GWRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | GENERIC_WRITE) +#define DIR_GEXEC (FILE_TRAVERSE | GENERIC_EXECUTE) + + /* standard owner (and administrator) rights */ + +#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \ + | SYNCHRONIZE \ + | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \ + | FILE_READ_EA | FILE_WRITE_EA) + + /* standard world rights */ + +#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \ + | SYNCHRONIZE) + + /* inheritance flags for files and directories */ + +#define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE +#define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE) + +/* + * To identify NTFS ACL meaning Posix ACL granted to root + * we use rights always granted to anybody, so they have no impact + * either on Windows or on Linux. + */ + +#define ROOT_OWNER_UNMARK SYNCHRONIZE /* ACL granted to root as owner */ +#define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */ + +/* + * A type large enough to hold any SID + */ + +typedef char BIGSID[40]; + +/* + * Struct to hold the input mapping file + * (private to this module) + */ + +struct MAPLIST { + struct MAPLIST *next; + char *uidstr; /* uid text from the same record */ + char *gidstr; /* gid text from the same record */ + char *sidstr; /* sid text from the same record */ + char maptext[LINESZ + 1]; +}; + +typedef int (*FILEREADER)(void *fileid, char *buf, size_t size, off_t pos); + +/* + * Constants defined in acls.c + */ + +extern const SID *adminsid; +extern const SID *worldsid; + +/* + * Functions defined in acls.c + */ + +BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz); +BOOL ntfs_valid_pattern(const SID *sid); +BOOL ntfs_valid_sid(const SID *sid); +BOOL ntfs_same_sid(const SID *first, const SID *second); + +BOOL ntfs_is_user_sid(const SID *usid); + + +int ntfs_sid_size(const SID * sid); +unsigned int ntfs_attr_size(const char *attr); + +const SID *ntfs_find_usid(const struct MAPPING *usermapping, + uid_t uid, SID *pdefsid); +const SID *ntfs_find_gsid(const struct MAPPING *groupmapping, + gid_t gid, SID *pdefsid); +uid_t ntfs_find_user(const struct MAPPING *usermapping, const SID *usid); +gid_t ntfs_find_group(const struct MAPPING *groupmapping, const SID * gsid); +const SID *ntfs_acl_owner(const char *secattr); + +#if POSIXACLS + +BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc); +void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc); +int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode); +struct POSIX_SECURITY *ntfs_build_inherited_posix( + const struct POSIX_SECURITY *pxdesc, mode_t mode, + mode_t umask, BOOL isdir); +struct POSIX_SECURITY *ntfs_build_basic_posix( + const struct POSIX_SECURITY *pxdesc, mode_t mode, + mode_t umask, BOOL isdir); +struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc, + const struct POSIX_ACL *newacl, int count, BOOL deflt); +struct POSIX_SECURITY *ntfs_build_permissions_posix( + struct MAPPING* const mapping[], + const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first, + const struct POSIX_SECURITY *second); +char *ntfs_build_descr_posix(struct MAPPING* const mapping[], + struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid); + +#endif /* POSIXACLS */ + +int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, + const SID *usid, const SID *gsid, + BOOL fordir, le16 inherited); +int ntfs_build_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +char *ntfs_build_descr(mode_t mode, + int isdir, const SID * usid, const SID * gsid); +struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid); +struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem); +struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem); +void ntfs_free_mapping(struct MAPPING *mapping[]); + +#endif /* ACLS_H */ + diff --git a/include/ntfs-3g/attrib.h b/include/ntfs-3g/attrib.h new file mode 100755 index 0000000000000000000000000000000000000000..b3752a608bc2fd1f879896d4ae79346aa2d06e26 --- /dev/null +++ b/include/ntfs-3g/attrib.h @@ -0,0 +1,403 @@ +/* + * attrib.h - Exports for attribute handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2006-2007 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_ATTRIB_H +#define _NTFS_ATTRIB_H + +/* Forward declarations */ +typedef struct _ntfs_attr ntfs_attr; +typedef struct _ntfs_attr_search_ctx ntfs_attr_search_ctx; + +#include "types.h" +#include "inode.h" +#include "unistr.h" +#include "runlist.h" +#include "volume.h" +#include "debug.h" +#include "logging.h" + +extern ntfschar AT_UNNAMED[]; +extern ntfschar STREAM_SDS[]; + +/* The little endian Unicode string $TXF_DATA as a global constant. */ +extern ntfschar TXF_DATA[10]; + +/** + * enum ntfs_lcn_special_values - special return values for ntfs_*_vcn_to_lcn() + * + * Special return values for ntfs_rl_vcn_to_lcn() and ntfs_attr_vcn_to_lcn(). + * + * TODO: Describe them. + */ +typedef enum { + LCN_HOLE = -1, /* Keep this as highest value or die! */ + LCN_RL_NOT_MAPPED = -2, + LCN_ENOENT = -3, + LCN_EINVAL = -4, + LCN_EIO = -5, +} ntfs_lcn_special_values; + +typedef enum { /* ways of processing holes when expanding */ + HOLES_NO, + HOLES_OK, + HOLES_DELAY, + HOLES_NONRES +} hole_type; + +/** + * struct ntfs_attr_search_ctx - search context used in attribute search functions + * @mrec: buffer containing mft record to search + * @attr: attribute record in @mrec where to begin/continue search + * @is_first: if true lookup_attr() begins search with @attr, else after @attr + * + * Structure must be initialized to zero before the first call to one of the + * attribute search functions. Initialize @mrec to point to the mft record to + * search, and @attr to point to the first attribute within @mrec (not necessary + * if calling the _first() functions), and set @is_first to TRUE (not necessary + * if calling the _first() functions). + * + * If @is_first is TRUE, the search begins with @attr. If @is_first is FALSE, + * the search begins after @attr. This is so that, after the first call to one + * of the search attribute functions, we can call the function again, without + * any modification of the search context, to automagically get the next + * matching attribute. + */ +struct _ntfs_attr_search_ctx { + MFT_RECORD *mrec; + ATTR_RECORD *attr; + BOOL is_first; + ntfs_inode *ntfs_ino; + ATTR_LIST_ENTRY *al_entry; + ntfs_inode *base_ntfs_ino; + MFT_RECORD *base_mrec; + ATTR_RECORD *base_attr; +}; + +extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx); +extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, + MFT_RECORD *mrec); +extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx); + +extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn, const u8 *val, const u32 val_len, + ntfs_attr_search_ctx *ctx); + +extern int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx); + +extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, + const ATTR_TYPES type); + +/** + * ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode + * @ctx: initialised attribute search context + * + * Syntactic sugar for walking attributes in an inode. + * + * Return 0 on success and -1 on error with errno set to the error code from + * ntfs_attr_lookup(). + * + * Example: When you want to enumerate all attributes in an open ntfs inode + * @ni, you can simply do: + * + * int err; + * ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL); + * if (!ctx) + * // Error code is in errno. Handle this case. + * while (!(err = ntfs_attrs_walk(ctx))) { + * ATTR_RECORD *attr = ctx->attr; + * // attr now contains the next attribute. Do whatever you want + * // with it and then just continue with the while loop. + * } + * if (err && errno != ENOENT) + * // Ooops. An error occurred! You should handle this case. + * // Now finished with all attributes in the inode. + */ +static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx) +{ + return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, + NULL, 0, ctx); +} + +/** + * struct ntfs_attr - ntfs in memory non-resident attribute structure + * @rl: if not NULL, the decompressed runlist + * @ni: base ntfs inode to which this attribute belongs + * @type: attribute type + * @name: Unicode name of the attribute + * @name_len: length of @name in Unicode characters + * @state: NTFS attribute specific flags describing this attribute + * @allocated_size: copy from the attribute record + * @data_size: copy from the attribute record + * @initialized_size: copy from the attribute record + * @compressed_size: copy from the attribute record + * @compression_block_size: size of a compression block (cb) + * @compression_block_size_bits: log2 of the size of a cb + * @compression_block_clusters: number of clusters per cb + * + * This structure exists purely to provide a mechanism of caching the runlist + * of an attribute. If you want to operate on a particular attribute extent, + * you should not be using this structure at all. If you want to work with a + * resident attribute, you should not be using this structure at all. As a + * fail-safe check make sure to test NAttrNonResident() and if it is false, you + * know you shouldn't be using this structure. + * + * If you want to work on a resident attribute or on a specific attribute + * extent, you should use ntfs_lookup_attr() to retrieve the attribute (extent) + * record, edit that, and then write back the mft record (or set the + * corresponding ntfs inode dirty for delayed write back). + * + * @rl is the decompressed runlist of the attribute described by this + * structure. Obviously this only makes sense if the attribute is not resident, + * i.e. NAttrNonResident() is true. If the runlist hasn't been decompressed yet + * @rl is NULL, so be prepared to cope with @rl == NULL. + * + * @ni is the base ntfs inode of the attribute described by this structure. + * + * @type is the attribute type (see layout.h for the definition of ATTR_TYPES), + * @name and @name_len are the little endian Unicode name and the name length + * in Unicode characters of the attribute, respectively. + * + * @state contains NTFS attribute specific flags describing this attribute + * structure. See ntfs_attr_state_bits above. + */ +struct _ntfs_attr { + runlist_element *rl; + ntfs_inode *ni; + ATTR_TYPES type; + ATTR_FLAGS data_flags; + ntfschar *name; + u32 name_len; + unsigned long state; + s64 allocated_size; + s64 data_size; + s64 initialized_size; + s64 compressed_size; + u32 compression_block_size; + u8 compression_block_size_bits; + u8 compression_block_clusters; + s8 unused_runs; /* pre-reserved entries available */ +}; + +/** + * enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr + * structure + */ +typedef enum { + NA_Initialized, /* 1: structure is initialized. */ + NA_NonResident, /* 1: Attribute is not resident. */ + NA_BeingNonResident, /* 1: Attribute is being made not resident. */ + NA_FullyMapped, /* 1: Attribute has been fully mapped */ + NA_DataAppending, /* 1: Attribute is being appended to */ + NA_ComprClosing, /* 1: Compressed attribute is being closed */ + NA_RunlistDirty, /* 1: Runlist has been updated */ +} ntfs_attr_state_bits; + +#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state) +#define set_nattr_flag(na, flag) set_bit(NA_##flag, (na)->state) +#define clear_nattr_flag(na, flag) clear_bit(NA_##flag, (na)->state) + +#define NAttrInitialized(na) test_nattr_flag(na, Initialized) +#define NAttrSetInitialized(na) set_nattr_flag(na, Initialized) +#define NAttrClearInitialized(na) clear_nattr_flag(na, Initialized) + +#define NAttrNonResident(na) test_nattr_flag(na, NonResident) +#define NAttrSetNonResident(na) set_nattr_flag(na, NonResident) +#define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident) + +#define NAttrBeingNonResident(na) test_nattr_flag(na, BeingNonResident) +#define NAttrSetBeingNonResident(na) set_nattr_flag(na, BeingNonResident) +#define NAttrClearBeingNonResident(na) clear_nattr_flag(na, BeingNonResident) + +#define NAttrFullyMapped(na) test_nattr_flag(na, FullyMapped) +#define NAttrSetFullyMapped(na) set_nattr_flag(na, FullyMapped) +#define NAttrClearFullyMapped(na) clear_nattr_flag(na, FullyMapped) + +#define NAttrDataAppending(na) test_nattr_flag(na, DataAppending) +#define NAttrSetDataAppending(na) set_nattr_flag(na, DataAppending) +#define NAttrClearDataAppending(na) clear_nattr_flag(na, DataAppending) + +#define NAttrRunlistDirty(na) test_nattr_flag(na, RunlistDirty) +#define NAttrSetRunlistDirty(na) set_nattr_flag(na, RunlistDirty) +#define NAttrClearRunlistDirty(na) clear_nattr_flag(na, RunlistDirty) + +#define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing) +#define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing) +#define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing) + +#define GenNAttrIno(func_name, flag) \ +extern int NAttr##func_name(ntfs_attr *na); \ +extern void NAttrSet##func_name(ntfs_attr *na); \ +extern void NAttrClear##func_name(ntfs_attr *na); + +GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) +GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) +GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) +#undef GenNAttrIno + +/** + * union attr_val - Union of all known attribute values + * + * For convenience. Used in the attr structure. + */ +typedef union { + u8 _default; /* Unnamed u8 to serve as default when just using + a_val without specifying any of the below. */ + STANDARD_INFORMATION std_inf; + ATTR_LIST_ENTRY al_entry; + FILE_NAME_ATTR filename; + OBJECT_ID_ATTR obj_id; + SECURITY_DESCRIPTOR_ATTR sec_desc; + VOLUME_NAME vol_name; + VOLUME_INFORMATION vol_inf; + DATA_ATTR data; + INDEX_ROOT index_root; + INDEX_BLOCK index_blk; + BITMAP_ATTR bmp; + REPARSE_POINT reparse; + EA_INFORMATION ea_inf; + EA_ATTR ea; + PROPERTY_SET property_set; + LOGGED_UTILITY_STREAM logged_util_stream; + EFS_ATTR_HEADER efs; +} attr_val; + +extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, + const ATTR_FLAGS data_flags, const BOOL encrypted, + const BOOL sparse, + const s64 allocated_size, const s64 data_size, + const s64 initialized_size, const s64 compressed_size, + const u8 compression_unit); + + /* warning : in the following "name" has to be freeable */ + /* or one of constants AT_UNNAMED, NTFS_INDEX_I30 or STREAM_SDS */ +extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len); +extern void ntfs_attr_close(ntfs_attr *na); + +extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, + void *b); +extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, + const void *b); +extern int ntfs_attr_pclose(ntfs_attr *na); + +extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len, s64 *data_size); + +extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, + const s64 bk_cnt, const u32 bk_size, void *dst); +extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, + s64 bk_cnt, const u32 bk_size, void *src); + +extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn); +extern int ntfs_attr_map_whole_runlist(ntfs_attr *na); + +extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn); +extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn); + +extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol, + const ATTR_TYPES type, const s64 size); +extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, + const ATTR_TYPES type); +int ntfs_attr_make_non_resident(ntfs_attr *na, + ntfs_attr_search_ctx *ctx); +int ntfs_attr_force_non_resident(ntfs_attr *na); +extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size); + +extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + const ntfschar *name, u8 name_len, const u8 *val, u32 size, + ATTR_FLAGS flags); +extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + const ntfschar *name, u8 name_len, VCN lowest_vcn, + int dataruns_size, ATTR_FLAGS flags); +extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx); + +extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, const u8 *val, s64 size); +extern int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, + const ntfschar *name, u8 name_len, ATTR_FLAGS flags, + ATTR_FLAGS mask); +extern int ntfs_attr_rm(ntfs_attr *na); + +extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); + +extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, + const u32 new_size); + +extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni); +extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra); + +extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn); + +extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); +extern int ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize); + +/** + * get_attribute_value_length - return the length of the value of an attribute + * @a: pointer to a buffer containing the attribute record + * + * Return the byte size of the attribute value of the attribute @a (as it + * would be after eventual decompression and filling in of holes if sparse). + * If we return 0, check errno. If errno is 0 the actual length was 0, + * otherwise errno describes the error. + * + * FIXME: Describe possible errnos. + */ +extern s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a); + +/** + * get_attribute_value - return the attribute value of an attribute + * @vol: volume on which the attribute is present + * @a: attribute to get the value of + * @b: destination buffer for the attribute value + * + * Make a copy of the attribute value of the attribute @a into the destination + * buffer @b. Note, that the size of @b has to be at least equal to the value + * returned by get_attribute_value_length(@a). + * + * Return number of bytes copied. If this is zero check errno. If errno is 0 + * then nothing was read due to a zero-length attribute value, otherwise + * errno describes the error. + */ +extern s64 ntfs_get_attribute_value(const ntfs_volume *vol, + const ATTR_RECORD *a, u8 *b); + +extern void ntfs_attr_name_free(char **name); +extern char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len); +extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, + const ntfschar *name, u32 name_len); +extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len); +extern s64 ntfs_attr_get_free_bits(ntfs_attr *na); +extern int ntfs_attr_data_read(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset); +extern int ntfs_attr_data_write(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + const char *buf, size_t size, off_t offset); +extern int ntfs_attr_shrink_size(ntfs_inode *ni, ntfschar *stream_name, + int stream_name_len, off_t offset); + +#endif /* defined _NTFS_ATTRIB_H */ + diff --git a/include/ntfs-3g/attrlist.h b/include/ntfs-3g/attrlist.h new file mode 100755 index 0000000000000000000000000000000000000000..2952e48b86577b41c8a1283b458006755ee4c312 --- /dev/null +++ b/include/ntfs-3g/attrlist.h @@ -0,0 +1,51 @@ +/* + * attrlist.h - Exports for attribute list attribute handling. + * Originated from Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2004 Yura Pakhuchiy + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_ATTRLIST_H +#define _NTFS_ATTRLIST_H + +#include "attrib.h" + +extern int ntfs_attrlist_need(ntfs_inode *ni); + +extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr); +extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx); + +/** + * ntfs_attrlist_mark_dirty - set the attribute list dirty + * @ni: ntfs inode which base inode contain dirty attribute list + * + * Set the attribute list dirty so it is written out later (at the latest at + * ntfs_inode_close() time). + * + * This function cannot fail. + */ +static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni) +{ + if (ni->nr_extents == -1) + NInoAttrListSetDirty(ni->base_ni); + else + NInoAttrListSetDirty(ni); +} + +#endif /* defined _NTFS_ATTRLIST_H */ diff --git a/include/ntfs-3g/bitmap.h b/include/ntfs-3g/bitmap.h new file mode 100755 index 0000000000000000000000000000000000000000..10b5f6c5b47c196d02627a0dcf58d1be33aa0cf0 --- /dev/null +++ b/include/ntfs-3g/bitmap.h @@ -0,0 +1,96 @@ +/* + * bitmap.h - Exports for bitmap handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_BITMAP_H +#define _NTFS_BITMAP_H + +#include "types.h" +#include "attrib.h" + +/* + * NOTES: + * + * - Operations are 8-bit only to ensure the functions work both on little + * and big endian machines! So don't make them 32-bit ops! + * - bitmap starts at bit = 0 and ends at bit = bitmap size - 1. + * - _Caller_ has to make sure that the bit to operate on is less than the + * size of the bitmap. + */ + +extern void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value); +extern char ntfs_bit_get(const u8 *bitmap, const u64 bit); +extern char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value); +extern int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count); +extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count); + +/** + * ntfs_bitmap_set_bit - set a bit in a bitmap + * @na: attribute containing the bitmap + * @bit: bit to set + * + * Set the @bit in the bitmap described by the attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit) +{ + return ntfs_bitmap_set_run(na, bit, 1); +} + +/** + * ntfs_bitmap_clear_bit - clear a bit in a bitmap + * @na: attribute containing the bitmap + * @bit: bit to clear + * + * Clear @bit in the bitmap described by the attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit) +{ + return ntfs_bitmap_clear_run(na, bit, 1); +} + +/* + * rol32 - rotate a 32-bit value left + * + * @word: value to rotate + * @shift: bits to roll + */ +static __inline__ u32 ntfs_rol32(u32 word, unsigned int shift) +{ + return (word << shift) | (word >> (32 - shift)); +} + +/* + * ror32 - rotate a 32-bit value right + * + * @word: value to rotate + * @shift: bits to roll + */ +static __inline__ u32 ntfs_ror32(u32 word, unsigned int shift) +{ + return (word >> shift) | (word << (32 - shift)); +} + +#endif /* defined _NTFS_BITMAP_H */ + diff --git a/include/ntfs-3g/bootsect.h b/include/ntfs-3g/bootsect.h new file mode 100755 index 0000000000000000000000000000000000000000..a299e821efac1d532f7fbefaea0362c1cba1e013 --- /dev/null +++ b/include/ntfs-3g/bootsect.h @@ -0,0 +1,42 @@ +/* + * bootsect.h - Exports for bootsector record handling. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov + * Copyright (c) 2006 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_BOOTSECT_H +#define _NTFS_BOOTSECT_H + +#include "types.h" +#include "volume.h" +#include "layout.h" + +/** + * ntfs_boot_sector_is_ntfs - check a boot sector for describing an ntfs volume + * @b: buffer containing the boot sector + * + * This function checks the boot sector in @b for describing a valid ntfs + * volume. Return TRUE if @b is a valid NTFS boot sector or FALSE otherwise. + */ +extern BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b); +extern int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs); + +#endif /* defined _NTFS_BOOTSECT_H */ + diff --git a/include/ntfs-3g/cache.h b/include/ntfs-3g/cache.h new file mode 100755 index 0000000000000000000000000000000000000000..be63b1a4bd7f37907e5f83745fd8500763aa26a5 --- /dev/null +++ b/include/ntfs-3g/cache.h @@ -0,0 +1,118 @@ +/* + * cache.h : deal with indexed LRU caches + * + * Copyright (c) 2008-2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_CACHE_H_ +#define _NTFS_CACHE_H_ + +#include "volume.h" + +struct CACHED_GENERIC { + struct CACHED_GENERIC *next; + struct CACHED_GENERIC *previous; + void *variable; + size_t varsize; + union ALIGNMENT payload[0]; +} ; + +struct CACHED_INODE { + struct CACHED_INODE *next; + struct CACHED_INODE *previous; + const char *pathname; + size_t varsize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; +} ; + +struct CACHED_NIDATA { + struct CACHED_NIDATA *next; + struct CACHED_NIDATA *previous; + const char *pathname; /* not used */ + size_t varsize; /* not used */ + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; + ntfs_inode *ni; +} ; + +struct CACHED_LOOKUP { + struct CACHED_LOOKUP *next; + struct CACHED_LOOKUP *previous; + const char *name; + size_t namesize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 parent; + u64 inum; +} ; + +enum { + CACHE_FREE = 1, + CACHE_NOHASH = 2 +} ; + +typedef int (*cache_compare)(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *item); +typedef void (*cache_free)(const struct CACHED_GENERIC *cached); +typedef int (*cache_hash)(const struct CACHED_GENERIC *cached); + +struct HASH_ENTRY { + struct HASH_ENTRY *next; + struct CACHED_GENERIC *entry; +} ; + +struct CACHE_HEADER { + const char *name; + struct CACHED_GENERIC *most_recent_entry; + struct CACHED_GENERIC *oldest_entry; + struct CACHED_GENERIC *free_entry; + struct HASH_ENTRY *free_hash; + struct HASH_ENTRY **first_hash; + cache_free dofree; + cache_hash dohash; + unsigned long reads; + unsigned long writes; + unsigned long hits; + int fixed_size; + int max_hash; + struct CACHED_GENERIC entry[0]; +} ; + + /* cast to generic, avoiding gcc warnings */ +#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr)) + +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, + cache_compare compare); +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare); +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare, int flags); +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags); + +void ntfs_create_lru_caches(ntfs_volume *vol); +void ntfs_free_lru_caches(ntfs_volume *vol); + +#endif /* _NTFS_CACHE_H_ */ + diff --git a/include/ntfs-3g/collate.h b/include/ntfs-3g/collate.h new file mode 100755 index 0000000000000000000000000000000000000000..fe38383509aee30e50a9ce547030b653c87ce16d --- /dev/null +++ b/include/ntfs-3g/collate.h @@ -0,0 +1,34 @@ +/* + * collate.h - Defines for NTFS collation handling. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_COLLATE_H +#define _NTFS_COLLATE_H + +#include "types.h" +#include "volume.h" + +#define NTFS_COLLATION_ERROR -2 + +extern COLLATE ntfs_get_collate_function(COLLATION_RULES); + +#endif /* _NTFS_COLLATE_H */ diff --git a/include/ntfs-3g/compat.h b/include/ntfs-3g/compat.h new file mode 100755 index 0000000000000000000000000000000000000000..ece7ba8fe13bad16aeb9d8ed775bdd1b54a93068 --- /dev/null +++ b/include/ntfs-3g/compat.h @@ -0,0 +1,75 @@ +/* + * compat.h - Tweaks for Windows compatibility. + * + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2008-2009 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_COMPAT_H +#define _NTFS_COMPAT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#include <errno.h> /* ENODATA */ + +#ifndef ENODATA +#define ENODATA ENOENT +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#ifndef HAVE_FFS +extern int ffs(int i); +#endif /* HAVE_FFS */ + +#ifndef HAVE_DAEMON +extern int daemon(int nochdir, int noclose); +#endif /* HAVE_DAEMON */ + +#ifndef HAVE_STRSEP +extern char *strsep(char **stringp, const char *delim); +#endif /* HAVE_STRSEP */ + +#ifdef WINDOWS + +#define HAVE_STDIO_H /* mimic config.h */ +#define HAVE_STDARG_H + +#define atoll _atoi64 +#define fdatasync commit +#define __inline__ inline +#define __attribute__(X) /*nothing*/ + +#else /* !defined WINDOWS */ + +#ifndef O_BINARY +#define O_BINARY 0 /* unix is binary by default */ +#endif + +#endif /* defined WINDOWS */ + +#endif /* defined _NTFS_COMPAT_H */ + diff --git a/include/ntfs-3g/compress.h b/include/ntfs-3g/compress.h new file mode 100755 index 0000000000000000000000000000000000000000..c256932169c9bb4fcc189cd5e9585f402f47b36a --- /dev/null +++ b/include/ntfs-3g/compress.h @@ -0,0 +1,41 @@ +/* + * compress.h - Exports for compressed attribute handling. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_COMPRESS_H +#define _NTFS_COMPRESS_H + +#include "types.h" +#include "attrib.h" + +extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, + void *b); + +extern s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *brl, s64 wpos, + s64 offs, s64 to_write, s64 rounded, + const void *b, int compressed_part, + VCN *update_from); + +extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl, + s64 offs, VCN *update_from); + +#endif /* defined _NTFS_COMPRESS_H */ + diff --git a/include/ntfs-3g/debug.h b/include/ntfs-3g/debug.h new file mode 100755 index 0000000000000000000000000000000000000000..f7f3c6fb4ebead8fd5123ae52dcd2bbc73c29df6 --- /dev/null +++ b/include/ntfs-3g/debug.h @@ -0,0 +1,47 @@ +/* + * debug.h - Debugging output functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2004 Anton Altaparmakov + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DEBUG_H +#define _NTFS_DEBUG_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "logging.h" + +struct _runlist_element; + +#ifdef DEBUG +extern void ntfs_debug_runlist_dump(const struct _runlist_element *rl); +#else +static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl __attribute__((unused))) {} +#endif + +#define NTFS_BUG(msg) \ +{ \ + int ___i = 1; \ + ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \ + ntfs_log_debug("Forcing segmentation fault!"); \ + ___i = ((int*)NULL)[___i]; \ +} + +#endif /* defined _NTFS_DEBUG_H */ diff --git a/include/ntfs-3g/device.h b/include/ntfs-3g/device.h new file mode 100755 index 0000000000000000000000000000000000000000..c7cc9b64f696dec00674ecd9f31ca255ccfd7de4 --- /dev/null +++ b/include/ntfs-3g/device.h @@ -0,0 +1,144 @@ +/* + * device.h - Exports for low level device io. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2013 Anton Altaparmakov + * Copyright (c) 2008-2013 Tuxera Inc. + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DEVICE_H +#define _NTFS_DEVICE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "device_io.h" +#include "types.h" +#include "support.h" +#include "volume.h" + +/** + * enum ntfs_device_state_bits - + * + * Defined bits for the state field in the ntfs_device structure. + */ +typedef enum { + ND_Open, /* 1: Device is open. */ + ND_ReadOnly, /* 1: Device is read-only. */ + ND_Dirty, /* 1: Device is dirty, needs sync. */ + ND_Block, /* 1: Device is a block device. */ + ND_Sync, /* 1: Device is mounted with "-o sync" */ +} ntfs_device_state_bits; + +#define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state) +#define set_ndev_flag(nd, flag) set_bit(ND_##flag, (nd)->d_state) +#define clear_ndev_flag(nd, flag) clear_bit(ND_##flag, (nd)->d_state) + +#define NDevOpen(nd) test_ndev_flag(nd, Open) +#define NDevSetOpen(nd) set_ndev_flag(nd, Open) +#define NDevClearOpen(nd) clear_ndev_flag(nd, Open) + +#define NDevReadOnly(nd) test_ndev_flag(nd, ReadOnly) +#define NDevSetReadOnly(nd) set_ndev_flag(nd, ReadOnly) +#define NDevClearReadOnly(nd) clear_ndev_flag(nd, ReadOnly) + +#define NDevDirty(nd) test_ndev_flag(nd, Dirty) +#define NDevSetDirty(nd) set_ndev_flag(nd, Dirty) +#define NDevClearDirty(nd) clear_ndev_flag(nd, Dirty) + +#define NDevBlock(nd) test_ndev_flag(nd, Block) +#define NDevSetBlock(nd) set_ndev_flag(nd, Block) +#define NDevClearBlock(nd) clear_ndev_flag(nd, Block) + +#define NDevSync(nd) test_ndev_flag(nd, Sync) +#define NDevSetSync(nd) set_ndev_flag(nd, Sync) +#define NDevClearSync(nd) clear_ndev_flag(nd, Sync) + +/** + * struct ntfs_device - + * + * The ntfs device structure defining all operations needed to access the low + * level device underlying the ntfs volume. + * + * Note d_heads and d_sectors_per_track are only set as a result of a call to + * either ntfs_device_heads_get() or ntfs_device_sectors_per_track_get() (both + * calls will set up both fields or if getting them failed they will be left at + * -1). + */ +struct ntfs_device { + struct ntfs_device_operations *d_ops; /* Device operations. */ + unsigned long d_state; /* State of the device. */ + char *d_name; /* Name of device. */ + void *d_private; /* Private data used by the + device operations. */ + int d_heads; /* Disk geometry: number of + heads or -1. */ + int d_sectors_per_track; /* Disk geometry: number of + sectors per track or -1. */ +}; + +struct stat; + +/** + * struct ntfs_device_operations - + * + * The ntfs device operations defining all operations that can be performed on + * the low level device described by an ntfs device structure. + */ +struct ntfs_device_operations { + int (*open)(struct ntfs_device *dev, int flags); + int (*close)(struct ntfs_device *dev); + s64 (*seek)(struct ntfs_device *dev, s64 offset, int whence); + s64 (*read)(struct ntfs_device *dev, void *buf, s64 count); + s64 (*write)(struct ntfs_device *dev, const void *buf, s64 count); + s64 (*pread)(struct ntfs_device *dev, void *buf, s64 count, s64 offset); + s64 (*pwrite)(struct ntfs_device *dev, const void *buf, s64 count, + s64 offset); + int (*sync)(struct ntfs_device *dev); + int (*stat)(struct ntfs_device *dev, struct stat *buf); + int (*ioctl)(struct ntfs_device *dev, int request, void *argp); +}; + +extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state, + struct ntfs_device_operations *dops, void *priv_data); +extern int ntfs_device_free(struct ntfs_device *dev); +extern int ntfs_device_sync(struct ntfs_device *dev); + +extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, + void *b); +extern s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const void *b); + +extern s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b); +extern s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b); + +extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, + const s64 count, void *b); +extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, + const s64 count, const void *b); + +extern s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size); +extern s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev); +extern int ntfs_device_heads_get(struct ntfs_device *dev); +extern int ntfs_device_sectors_per_track_get(struct ntfs_device *dev); +extern int ntfs_device_sector_size_get(struct ntfs_device *dev); +extern int ntfs_device_block_size_set(struct ntfs_device *dev, int block_size); + +#endif /* defined _NTFS_DEVICE_H */ diff --git a/include/ntfs-3g/device_io.h b/include/ntfs-3g/device_io.h new file mode 100755 index 0000000000000000000000000000000000000000..66ad2439fb1961a3491193e5c7c84ea0ebcaba05 --- /dev/null +++ b/include/ntfs-3g/device_io.h @@ -0,0 +1,88 @@ +/* + * device_io.h - Exports for default device io. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DEVICE_IO_H +#define _NTFS_DEVICE_IO_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS + +#if defined(linux) || defined(__uClinux__) || defined(__sun) \ + || defined(__APPLE__) || defined(__DARWIN__) + /* Make sure the presence of <windows.h> means compiling for Windows */ +#undef HAVE_WINDOWS_H +#endif + +#ifndef HAVE_WINDOWS_H + +/* Not for Windows use standard Unix style low level device operations. */ +#define ntfs_device_default_io_ops ntfs_device_unix_io_ops + +#else /* HAVE_WINDOWS_H */ + +#ifndef HDIO_GETGEO +# define HDIO_GETGEO 0x301 +/** + * struct hd_geometry - + */ +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; +}; +#endif +#ifndef BLKGETSIZE +# define BLKGETSIZE 0x1260 +#endif +#ifndef BLKSSZGET +# define BLKSSZGET 0x1268 +#endif +#ifndef BLKGETSIZE64 +# define BLKGETSIZE64 0x80041272 +#endif +#ifndef BLKBSZSET +# define BLKBSZSET 0x40041271 +#endif + +/* On Windows (and Cygwin) : use Win32 low level device operations. */ +#define ntfs_device_default_io_ops ntfs_device_win32_io_ops + +/* A few useful functions */ +int ntfs_win32_set_sparse(int); +int ntfs_win32_ftruncate(int fd, s64 size); +int ntfs_device_win32_ftruncate(struct ntfs_device*, s64); + +#endif /* HAVE_WINDOWS_H */ + + +/* Forward declaration. */ +struct ntfs_device_operations; + +extern struct ntfs_device_operations ntfs_device_default_io_ops; + +#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */ + +#endif /* defined _NTFS_DEVICE_IO_H */ + diff --git a/include/ntfs-3g/dir.h b/include/ntfs-3g/dir.h new file mode 100755 index 0000000000000000000000000000000000000000..a99c1ae0d733c7e130f03025658974b574e9f258 --- /dev/null +++ b/include/ntfs-3g/dir.h @@ -0,0 +1,129 @@ +/* + * dir.h - Exports for directory handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2008 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_DIR_H +#define _NTFS_DIR_H + +#include "types.h" + +#define PATH_SEP '/' + +/* + * We do not have these under DJGPP, so define our version that do not conflict + * with other S_IFs defined under DJGPP. + */ +#ifdef DJGPP +#ifndef S_IFLNK +#define S_IFLNK 0120000 +#endif +#ifndef S_ISLNK +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#ifndef S_IFSOCK +#define S_IFSOCK 0140000 +#endif +#ifndef S_ISSOCK +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#endif + +/* + * The little endian Unicode strings $I30, $SII, $SDH, $O, $Q, $R + * as a global constant. + */ +extern ntfschar NTFS_INDEX_I30[5]; +extern ntfschar NTFS_INDEX_SII[5]; +extern ntfschar NTFS_INDEX_SDH[5]; +extern ntfschar NTFS_INDEX_O[3]; +extern ntfschar NTFS_INDEX_Q[3]; +extern ntfschar NTFS_INDEX_R[3]; + +extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, + const ntfschar *uname, const int uname_len); +extern u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name); +extern void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, + u64 inum); + +extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, + const char *pathname); +extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, + const ntfschar *name, u8 name_len, mode_t type); +extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid, + const ntfschar *name, u8 name_len, mode_t type, dev_t dev); +extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid, + const ntfschar *name, u8 name_len, const ntfschar *target, + int target_len); +extern int ntfs_check_empty_dir(ntfs_inode *ni); +extern int ntfs_delete(ntfs_volume *vol, const char *path, + ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, + u8 name_len); + +extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, + u8 name_len); + +/* + * File types (adapted from include <linux/fs.h>) + */ +#define NTFS_DT_UNKNOWN 0 +#define NTFS_DT_FIFO 1 +#define NTFS_DT_CHR 2 +#define NTFS_DT_DIR 4 +#define NTFS_DT_BLK 6 +#define NTFS_DT_REG 8 +#define NTFS_DT_LNK 10 +#define NTFS_DT_SOCK 12 +#define NTFS_DT_WHT 14 + +/* + * This is the "ntfs_filldir" function type, used by ntfs_readdir() to let + * the caller specify what kind of dirent layout it wants to have. + * This allows the caller to read directories into their application or + * to have different dirent layouts depending on the binary type. + */ +typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name, + const int name_len, const int name_type, const s64 pos, + const MFT_REF mref, const unsigned dt_type); + +extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, + void *dirent, ntfs_filldir_t filldir); + +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni); + +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size); +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags); +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni); + +#if CACHE_INODE_SIZE + +struct CACHED_GENERIC; + +extern int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached); +extern int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached); + +#endif + +#endif /* defined _NTFS_DIR_H */ + diff --git a/include/ntfs-3g/ea.h b/include/ntfs-3g/ea.h new file mode 100755 index 0000000000000000000000000000000000000000..1936aab493b0bf11bc79b8508a52c11dcd5f67fb --- /dev/null +++ b/include/ntfs-3g/ea.h @@ -0,0 +1,33 @@ +/* + * + * Copyright (c) 2014 Jean-Pierre Andre + * + */ + +/* + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EA_H +#define EA_H + +int ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags); + +int ntfs_remove_ntfs_ea(ntfs_inode *ni); + +#endif /* EA_H */ diff --git a/include/ntfs-3g/efs.h b/include/ntfs-3g/efs.h new file mode 100755 index 0000000000000000000000000000000000000000..6eada0675959339ff3a6dbcd173292a6de12d9d1 --- /dev/null +++ b/include/ntfs-3g/efs.h @@ -0,0 +1,30 @@ +/* + * + * Copyright (c) 2009 Martin Bene + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EFS_H +#define EFS_H + +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_efs_info(ntfs_inode *ni, + const char *value, size_t size, int flags); +int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na); + +#endif /* EFS_H */ diff --git a/include/ntfs-3g/endians.h b/include/ntfs-3g/endians.h new file mode 100755 index 0000000000000000000000000000000000000000..f00089372ba8ad99d6973b989da99b4efe4faafb --- /dev/null +++ b/include/ntfs-3g/endians.h @@ -0,0 +1,205 @@ +/* + * endians.h - Definitions related to handling of byte ordering. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_ENDIANS_H +#define _NTFS_ENDIANS_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* + * Notes: + * We define the conversion functions including typecasts since the + * defaults don't necessarily perform appropriate typecasts. + * Also, using our own functions means that we can change them if it + * turns out that we do need to use the unaligned access macros on + * architectures requiring aligned memory accesses... + */ + +#ifdef HAVE_ENDIAN_H +#include <endian.h> +#endif +#ifdef HAVE_SYS_ENDIAN_H +#include <sys/endian.h> +#endif +#ifdef HAVE_MACHINE_ENDIAN_H +#include <machine/endian.h> +#endif +#ifdef HAVE_SYS_BYTEORDER_H +#include <sys/byteorder.h> +#endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#define __BYTE_ORDER BYTE_ORDER + +#ifndef __BYTE_ORDER +# if defined(_BYTE_ORDER) +# define __BYTE_ORDER _BYTE_ORDER +# define __LITTLE_ENDIAN _LITTLE_ENDIAN +# define __BIG_ENDIAN _BIG_ENDIAN +# elif defined(BYTE_ORDER) +# define __BYTE_ORDER BYTE_ORDER +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __BIG_ENDIAN BIG_ENDIAN +# elif defined(__BYTE_ORDER__) +# define __BYTE_ORDER __BYTE_ORDER__ +# define __LITTLE_ENDIAN __LITTLE_ENDIAN__ +# define __BIG_ENDIAN __BIG_ENDIAN__ +# elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \ + defined(WORDS_LITTLEENDIAN) +# define __BYTE_ORDER 1 +# define __LITTLE_ENDIAN 1 +# define __BIG_ENDIAN 0 +# elif (!defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) || \ + defined(WORDS_BIGENDIAN) +# define __BYTE_ORDER 0 +# define __LITTLE_ENDIAN 1 +# define __BIG_ENDIAN 0 +# else +# error "__BYTE_ORDER is not defined." +# endif +#endif + +#define __ntfs_bswap_constant_16(x) \ + (u16)((((u16)(x) & 0xff00) >> 8) | \ + (((u16)(x) & 0x00ff) << 8)) + +#define __ntfs_bswap_constant_32(x) \ + (u32)((((u32)(x) & 0xff000000u) >> 24) | \ + (((u32)(x) & 0x00ff0000u) >> 8) | \ + (((u32)(x) & 0x0000ff00u) << 8) | \ + (((u32)(x) & 0x000000ffu) << 24)) + +#define __ntfs_bswap_constant_64(x) \ + (u64)((((u64)(x) & 0xff00000000000000ull) >> 56) | \ + (((u64)(x) & 0x00ff000000000000ull) >> 40) | \ + (((u64)(x) & 0x0000ff0000000000ull) >> 24) | \ + (((u64)(x) & 0x000000ff00000000ull) >> 8) | \ + (((u64)(x) & 0x00000000ff000000ull) << 8) | \ + (((u64)(x) & 0x0000000000ff0000ull) << 24) | \ + (((u64)(x) & 0x000000000000ff00ull) << 40) | \ + (((u64)(x) & 0x00000000000000ffull) << 56)) + +#ifdef HAVE_BYTESWAP_H +# include <byteswap.h> +#else +# define bswap_16(x) __ntfs_bswap_constant_16(x) +# define bswap_32(x) __ntfs_bswap_constant_32(x) +# define bswap_64(x) __ntfs_bswap_constant_64(x) +#endif + +#if defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) + +#define __le16_to_cpu(x) (x) +#define __le32_to_cpu(x) (x) +#define __le64_to_cpu(x) (x) + +#define __cpu_to_le16(x) (x) +#define __cpu_to_le32(x) (x) +#define __cpu_to_le64(x) (x) + +#define __constant_le16_to_cpu(x) (x) +#define __constant_le32_to_cpu(x) (x) +#define __constant_le64_to_cpu(x) (x) + +#define __constant_cpu_to_le16(x) (x) +#define __constant_cpu_to_le32(x) (x) +#define __constant_cpu_to_le64(x) (x) + +#elif defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) + +#define __le16_to_cpu(x) bswap_16(x) +#define __le32_to_cpu(x) bswap_32(x) +#define __le64_to_cpu(x) bswap_64(x) + +#define __cpu_to_le16(x) bswap_16(x) +#define __cpu_to_le32(x) bswap_32(x) +#define __cpu_to_le64(x) bswap_64(x) + +#define __constant_le16_to_cpu(x) __ntfs_bswap_constant_16((u16)(x)) +#define __constant_le32_to_cpu(x) __ntfs_bswap_constant_32((u32)(x)) +#define __constant_le64_to_cpu(x) __ntfs_bswap_constant_64((u64)(x)) + +#define __constant_cpu_to_le16(x) __ntfs_bswap_constant_16((u16)(x)) +#define __constant_cpu_to_le32(x) __ntfs_bswap_constant_32((u32)(x)) +#define __constant_cpu_to_le64(x) __ntfs_bswap_constant_64((u64)(x)) + +#else + +#error "You must define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN." + +#endif + +/* Unsigned from LE to CPU conversion. */ + +#define le16_to_cpu(x) (u16)__le16_to_cpu((u16)(x)) +#define le32_to_cpu(x) (u32)__le32_to_cpu((u32)(x)) +#define le64_to_cpu(x) (u64)__le64_to_cpu((u64)(x)) + +#define le16_to_cpup(x) (u16)__le16_to_cpu(*(const u16*)(x)) +#define le32_to_cpup(x) (u32)__le32_to_cpu(*(const u32*)(x)) +#define le64_to_cpup(x) (u64)__le64_to_cpu(*(const u64*)(x)) + +/* Signed from LE to CPU conversion. */ + +#define sle16_to_cpu(x) (s16)__le16_to_cpu((s16)(x)) +#define sle32_to_cpu(x) (s32)__le32_to_cpu((s32)(x)) +#define sle64_to_cpu(x) (s64)__le64_to_cpu((s64)(x)) + +#define sle16_to_cpup(x) (s16)__le16_to_cpu(*(s16*)(x)) +#define sle32_to_cpup(x) (s32)__le32_to_cpu(*(s32*)(x)) +#define sle64_to_cpup(x) (s64)__le64_to_cpu(*(s64*)(x)) + +/* Unsigned from CPU to LE conversion. */ + +#define cpu_to_le16(x) (u16)__cpu_to_le16((u16)(x)) +#define cpu_to_le32(x) (u32)__cpu_to_le32((u32)(x)) +#define cpu_to_le64(x) (u64)__cpu_to_le64((u64)(x)) + +#define cpu_to_le16p(x) (u16)__cpu_to_le16(*(u16*)(x)) +#define cpu_to_le32p(x) (u32)__cpu_to_le32(*(u32*)(x)) +#define cpu_to_le64p(x) (u64)__cpu_to_le64(*(u64*)(x)) + +/* Signed from CPU to LE conversion. */ + +#define cpu_to_sle16(x) (s16)__cpu_to_le16((s16)(x)) +#define cpu_to_sle32(x) (s32)__cpu_to_le32((s32)(x)) +#define cpu_to_sle64(x) (s64)__cpu_to_le64((s64)(x)) + +#define cpu_to_sle16p(x) (s16)__cpu_to_le16(*(s16*)(x)) +#define cpu_to_sle32p(x) (s32)__cpu_to_le32(*(s32*)(x)) +#define cpu_to_sle64p(x) (s64)__cpu_to_le64(*(s64*)(x)) + +/* Constant endianness conversion defines. */ + +#define const_le16_to_cpu(x) __constant_le16_to_cpu(x) +#define const_le32_to_cpu(x) __constant_le32_to_cpu(x) +#define const_le64_to_cpu(x) __constant_le64_to_cpu(x) + +#define const_cpu_to_le16(x) __constant_cpu_to_le16(x) +#define const_cpu_to_le32(x) __constant_cpu_to_le32(x) +#define const_cpu_to_le64(x) __constant_cpu_to_le64(x) + +#endif /* defined _NTFS_ENDIANS_H */ diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h new file mode 100755 index 0000000000000000000000000000000000000000..c0e7618038a1a33e355a158e6a946f15117795b6 --- /dev/null +++ b/include/ntfs-3g/index.h @@ -0,0 +1,167 @@ +/* + * index.h - Defines for NTFS index handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2006-2008 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_INDEX_H +#define _NTFS_INDEX_H + +/* Convenience macros to test the versions of gcc. + * Use them like this: + * #if __GNUC_PREREQ (2,8) + * ... code requiring gcc 2.8 or later ... + * #endif + * Note - they won't work for gcc1 or glibc1, since the _MINOR macros + * were not defined then. + */ + +#ifndef __GNUC_PREREQ +# if defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +# else +# define __GNUC_PREREQ(maj, min) 0 +# endif +#endif + +/* allows us to warn about unused results of certain function calls */ +#ifndef __attribute_warn_unused_result__ +# if __GNUC_PREREQ (3,4) +# define __attribute_warn_unused_result__ \ + __attribute__ ((__warn_unused_result__)) +# else +# define __attribute_warn_unused_result__ /* empty */ +# endif +#endif + +#include "attrib.h" +#include "types.h" +#include "layout.h" +#include "inode.h" +#include "mft.h" + +#define VCN_INDEX_ROOT_PARENT ((VCN)-2) + +#define MAX_PARENT_VCN 32 + +typedef int (*COLLATE)(ntfs_volume *vol, const void *data1, int len1, + const void *data2, int len2); + +/** + * struct ntfs_index_context - + * @ni: inode containing the @entry described by this context + * @name: name of the index described by this context + * @name_len: length of the index name + * @entry: index entry (points into @ir or @ia) + * @data: index entry data (points into @entry) + * @data_len: length in bytes of @data + * @is_in_root: TRUE if @entry is in @ir or FALSE if it is in @ia + * @ir: index root if @is_in_root or NULL otherwise + * @actx: attribute search context if in root or NULL otherwise + * @ia: index block if @is_in_root is FALSE or NULL otherwise + * @ia_na: opened INDEX_ALLOCATION attribute + * @parent_pos: parent entries' positions in the index block + * @parent_vcn: entry's parent node or VCN_INDEX_ROOT_PARENT + * @new_vcn: new VCN if we need to create a new index block + * @median: move to the parent if splitting index blocks + * @ib_dirty: TRUE if index block was changed + * @block_size: index block size + * @vcn_size_bits: VCN size bits for this index block + * + * @ni is the inode this context belongs to. + * + * @entry is the index entry described by this context. @data and @data_len + * are the index entry data and its length in bytes, respectively. @data + * simply points into @entry. This is probably what the user is interested in. + * + * If @is_in_root is TRUE, @entry is in the index root attribute @ir described + * by the attribute search context @actx and inode @ni. @ia and + * @ib_dirty are undefined in this case. + * + * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia + * point to the index allocation block and VCN where it's placed, + * respectively. @ir and @actx are NULL in this case. @ia_na is opened + * INDEX_ALLOCATION attribute. @ib_dirty is TRUE if index block was changed and + * FALSE otherwise. + * + * To obtain a context call ntfs_index_ctx_get(). + * + * When finished with the @entry and its @data, call ntfs_index_ctx_put() to + * free the context and other associated resources. + * + * If the index entry was modified, call ntfs_index_entry_mark_dirty() before + * the call to ntfs_index_ctx_put() to ensure that the changes are written + * to disk. + */ +typedef struct { + ntfs_inode *ni; + ntfschar *name; + u32 name_len; + INDEX_ENTRY *entry; + void *data; + u16 data_len; + COLLATE collate; + BOOL is_in_root; + INDEX_ROOT *ir; + ntfs_attr_search_ctx *actx; + INDEX_BLOCK *ib; + ntfs_attr *ia_na; + int parent_pos[MAX_PARENT_VCN]; /* parent entries' positions */ + VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */ + int pindex; /* maximum it's the number of the parent nodes */ + BOOL ib_dirty; + u32 block_size; + u8 vcn_size_bits; +} ntfs_index_context; + +extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, + ntfschar *name, u32 name_len); +extern void ntfs_index_ctx_put(ntfs_index_context *ictx); +extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx); + +extern int ntfs_index_lookup(const void *key, const int key_len, + ntfs_index_context *ictx) __attribute_warn_unused_result__; + +extern INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie, + ntfs_index_context *ictx); + +extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, + MFT_REF mref); +extern int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni, + const void *key, const int keylen); + +extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr); + +extern VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie); + +extern void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx); + +extern char *ntfs_ie_filename_get(INDEX_ENTRY *ie); +extern void ntfs_ie_filename_dump(INDEX_ENTRY *ie); +extern void ntfs_ih_filename_dump(INDEX_HEADER *ih); + +/* the following was added by JPA for use in security.c */ +extern int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie); +extern int ntfs_index_rm(ntfs_index_context *icx); + +#endif /* _NTFS_INDEX_H */ + diff --git a/include/ntfs-3g/inode.h b/include/ntfs-3g/inode.h new file mode 100755 index 0000000000000000000000000000000000000000..5a6f7da66e1210fb3062b474167cbca4d76ce693 --- /dev/null +++ b/include/ntfs-3g/inode.h @@ -0,0 +1,225 @@ +/* + * inode.h - Defines for NTFS inode handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2001-2004 Anton Altaparmakov + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2006-2008 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_INODE_H +#define _NTFS_INODE_H + +/* Forward declaration */ +typedef struct _ntfs_inode ntfs_inode; + +#include "types.h" +#include "layout.h" +#include "support.h" +#include "volume.h" +#include "ntfstime.h" + +/** + * enum ntfs_inode_state_bits - + * + * Defined bits for the state field in the ntfs_inode structure. + * (f) = files only, (d) = directories only + */ +typedef enum { + NI_Dirty, /* 1: Mft record needs to be written to disk. */ + + /* The NI_AttrList* tests only make sense for base inodes. */ + NI_AttrList, /* 1: Mft record contains an attribute list. */ + NI_AttrListDirty, /* 1: Attribute list needs to be written to the + mft record and then to disk. */ + NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated + in the index. */ + NI_v3_Extensions, /* 1: JPA v3.x extensions present. */ + NI_TimesSet, /* 1: Use times which were set */ + NI_KnownSize, /* 1: Set if sizes are meaningful */ +} ntfs_inode_state_bits; + +#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state) +#define set_nino_flag(ni, flag) set_bit(NI_##flag, (ni)->state) +#define clear_nino_flag(ni, flag) clear_bit(NI_##flag, (ni)->state) + +#define test_and_set_nino_flag(ni, flag) \ + test_and_set_bit(NI_##flag, (ni)->state) +#define test_and_clear_nino_flag(ni, flag) \ + test_and_clear_bit(NI_##flag, (ni)->state) + +#define NInoDirty(ni) test_nino_flag(ni, Dirty) +#define NInoSetDirty(ni) set_nino_flag(ni, Dirty) +#define NInoClearDirty(ni) clear_nino_flag(ni, Dirty) +#define NInoTestAndSetDirty(ni) test_and_set_nino_flag(ni, Dirty) +#define NInoTestAndClearDirty(ni) test_and_clear_nino_flag(ni, Dirty) + +#define NInoAttrList(ni) test_nino_flag(ni, AttrList) +#define NInoSetAttrList(ni) set_nino_flag(ni, AttrList) +#define NInoClearAttrList(ni) clear_nino_flag(ni, AttrList) + + +#define test_nino_al_flag(ni, flag) test_nino_flag(ni, AttrList##flag) +#define set_nino_al_flag(ni, flag) set_nino_flag(ni, AttrList##flag) +#define clear_nino_al_flag(ni, flag) clear_nino_flag(ni, AttrList##flag) + +#define test_and_set_nino_al_flag(ni, flag) \ + test_and_set_nino_flag(ni, AttrList##flag) +#define test_and_clear_nino_al_flag(ni, flag) \ + test_and_clear_nino_flag(ni, AttrList##flag) + +#define NInoAttrListDirty(ni) test_nino_al_flag(ni, Dirty) +#define NInoAttrListSetDirty(ni) set_nino_al_flag(ni, Dirty) +#define NInoAttrListClearDirty(ni) clear_nino_al_flag(ni, Dirty) +#define NInoAttrListTestAndSetDirty(ni) test_and_set_nino_al_flag(ni, Dirty) +#define NInoAttrListTestAndClearDirty(ni) test_and_clear_nino_al_flag(ni, Dirty) + +#define NInoFileNameDirty(ni) test_nino_flag(ni, FileNameDirty) +#define NInoFileNameSetDirty(ni) set_nino_flag(ni, FileNameDirty) +#define NInoFileNameClearDirty(ni) clear_nino_flag(ni, FileNameDirty) +#define NInoFileNameTestAndSetDirty(ni) \ + test_and_set_nino_flag(ni, FileNameDirty) +#define NInoFileNameTestAndClearDirty(ni) \ + test_and_clear_nino_flag(ni, FileNameDirty) + +/** + * struct _ntfs_inode - The NTFS in-memory inode structure. + * + * It is just used as an extension to the fields already provided in the VFS + * inode. + */ +struct _ntfs_inode { + u64 mft_no; /* Inode / mft record number. */ + MFT_RECORD *mrec; /* The actual mft record of the inode. */ + ntfs_volume *vol; /* Pointer to the ntfs volume of this inode. */ + unsigned long state; /* NTFS specific flags describing this inode. + See ntfs_inode_state_bits above. */ + FILE_ATTR_FLAGS flags; /* Flags describing the file. + (Copy from STANDARD_INFORMATION) */ + /* + * Attribute list support (for use by the attribute lookup functions). + * Setup during ntfs_open_inode() for all inodes with attribute lists. + * Only valid if NI_AttrList is set in state. + */ + u32 attr_list_size; /* Length of attribute list value in bytes. */ + u8 *attr_list; /* Attribute list value itself. */ + /* Below fields are always valid. */ + s32 nr_extents; /* For a base mft record, the number of + attached extent inodes (0 if none), for + extent records this is -1. */ + union { /* This union is only used if nr_extents != 0. */ + ntfs_inode **extent_nis;/* For nr_extents > 0, array of the + ntfs inodes of the extent mft + records belonging to this base + inode which have been loaded. */ + ntfs_inode *base_ni; /* For nr_extents == -1, the ntfs + inode of the base mft record. */ + }; + + /* Below fields are valid only for base inode. */ + + /* + * These two fields are used to sync filename index and guaranteed to be + * correct, however value in index itself maybe wrong (windows itself + * do not update them properly). + * For directories, they hold the index size, provided the + * flag KnownSize is set. + */ + s64 data_size; /* Data size of unnamed DATA attribute + (or INDEX_ROOT for directories) */ + s64 allocated_size; /* Allocated size stored in the filename + index. (NOTE: Equal to allocated size of + the unnamed data attribute for normal or + encrypted files and to compressed size + of the unnamed data attribute for sparse or + compressed files.) */ + + /* + * These four fields are copy of relevant fields from + * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME + * attribute in the index. + */ + ntfs_time creation_time; + ntfs_time last_data_change_time; + ntfs_time last_mft_change_time; + ntfs_time last_access_time; + /* NTFS 3.x extensions added by JPA */ + /* only if NI_v3_Extensions is set in state */ + le32 owner_id; + le32 security_id; + le64 quota_charged; + le64 usn; +}; + +typedef enum { + NTFS_UPDATE_ATIME = 1 << 0, + NTFS_UPDATE_MTIME = 1 << 1, + NTFS_UPDATE_CTIME = 1 << 2, +} ntfs_time_update_flags; + +#define NTFS_UPDATE_MCTIME (NTFS_UPDATE_MTIME | NTFS_UPDATE_CTIME) +#define NTFS_UPDATE_AMCTIME (NTFS_UPDATE_ATIME | NTFS_UPDATE_MCTIME) + +extern ntfs_inode *ntfs_inode_base(ntfs_inode *ni); + +extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); + +extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref); + +extern int ntfs_inode_close(ntfs_inode *ni); +extern int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni); + +#if CACHE_NIDATA_SIZE + +struct CACHED_GENERIC; + +extern int ntfs_inode_real_close(ntfs_inode *ni); +extern void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref); +extern void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached); +extern int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item); + +#endif + + +extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, + const MFT_REF mref); + +extern int ntfs_inode_attach_all_extents(ntfs_inode *ni); + +extern void ntfs_inode_mark_dirty(ntfs_inode *ni); + +extern void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask); + +extern int ntfs_inode_sync(ntfs_inode *ni); + +extern int ntfs_inode_add_attrlist(ntfs_inode *ni); + +extern int ntfs_inode_free_space(ntfs_inode *ni, int size); + +extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a); + +extern int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size); + +extern int ntfs_inode_set_times(ntfs_inode *ni, const char *value, + size_t size, int flags); + +/* debugging */ +#define debug_double_inode(num, type) +#define debug_cached_inode(ni) + +#endif /* defined _NTFS_INODE_H */ diff --git a/include/ntfs-3g/ioctl.h b/include/ntfs-3g/ioctl.h new file mode 100755 index 0000000000000000000000000000000000000000..4ed6c01085626dc1296badb1066740624cb5cf2e --- /dev/null +++ b/include/ntfs-3g/ioctl.h @@ -0,0 +1,30 @@ +/* + * + * Copyright (c) 2014 Jean-Pierre Andre + * + */ + +/* + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef IOCTL_H +#define IOCTL_H + +int ntfs_ioctl(ntfs_inode *ni, int cmd, void *arg, + unsigned int flags, void *data); + +#endif /* IOCTL_H */ diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h new file mode 100755 index 0000000000000000000000000000000000000000..5b5fff6e90224aed826bd2acce6226bda8ce5dd6 --- /dev/null +++ b/include/ntfs-3g/layout.h @@ -0,0 +1,2663 @@ +/* + * layout.h - Ntfs on-disk layout structures. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_LAYOUT_H +#define _NTFS_LAYOUT_H + +#include "types.h" +#include "endians.h" +#include "support.h" + +/* The NTFS oem_id */ +#define magicNTFS const_cpu_to_le64(0x202020205346544e) /* "NTFS " */ +#define NTFS_SB_MAGIC 0x5346544e /* 'NTFS' */ + +/* + * Location of bootsector on partition: + * The standard NTFS_BOOT_SECTOR is on sector 0 of the partition. + * On NT4 and above there is one backup copy of the boot sector to + * be found on the last sector of the partition (not normally accessible + * from within Windows as the bootsector contained number of sectors + * value is one less than the actual value!). + * On versions of NT 3.51 and earlier, the backup copy was located at + * number of sectors/2 (integer divide), i.e. in the middle of the volume. + */ + +/** + * struct BIOS_PARAMETER_BLOCK - BIOS parameter block (bpb) structure. + */ +typedef struct { + u16 bytes_per_sector; /* Size of a sector in bytes. */ + u8 sectors_per_cluster; /* Size of a cluster in sectors. */ + u16 reserved_sectors; /* zero */ + u8 fats; /* zero */ + u16 root_entries; /* zero */ + u16 sectors; /* zero */ + u8 media_type; /* 0xf8 = hard disk */ + u16 sectors_per_fat; /* zero */ +/*0x0d*/u16 sectors_per_track; /* Required to boot Windows. */ +/*0x0f*/u16 heads; /* Required to boot Windows. */ +/*0x11*/u32 hidden_sectors; /* Offset to the start of the partition + relative to the disk in sectors. + Required to boot Windows. */ +/*0x15*/u32 large_sectors; /* zero */ +/* sizeof() = 25 (0x19) bytes */ +} __attribute__((__packed__)) BIOS_PARAMETER_BLOCK; + +/** + * struct NTFS_BOOT_SECTOR - NTFS boot sector structure. + */ +typedef struct { + u8 jump[3]; /* Irrelevant (jump to boot up code).*/ + u64 oem_id; /* Magic "NTFS ". */ +/*0x0b*/BIOS_PARAMETER_BLOCK bpb; /* See BIOS_PARAMETER_BLOCK. */ + u8 physical_drive; /* 0x00 floppy, 0x80 hard disk */ + u8 current_head; /* zero */ + u8 extended_boot_signature; /* 0x80 */ + u8 reserved2; /* zero */ +/*0x28*/s64 number_of_sectors; /* Number of sectors in volume. Gives + maximum volume size of 2^63 sectors. + Assuming standard sector size of 512 + bytes, the maximum byte size is + approx. 4.7x10^21 bytes. (-; */ + s64 mft_lcn; /* Cluster location of mft data. */ + s64 mftmirr_lcn; /* Cluster location of copy of mft. */ + s8 clusters_per_mft_record; /* Mft record size in clusters. */ + u8 reserved0[3]; /* zero */ + s8 clusters_per_index_record; /* Index block size in clusters. */ + u8 reserved1[3]; /* zero */ + u64 volume_serial_number; /* Irrelevant (serial number). */ + u32 checksum; /* Boot sector checksum. */ +/*0x54*/u8 bootstrap[426]; /* Irrelevant (boot up code). */ + u16 end_of_sector_marker; /* End of bootsector magic. Always is + 0xaa55 in little endian. */ +/* sizeof() = 512 (0x200) bytes */ +} __attribute__((__packed__)) NTFS_BOOT_SECTOR; + +/** + * enum NTFS_RECORD_TYPES - + * + * Magic identifiers present at the beginning of all ntfs record containing + * records (like mft records for example). + */ +typedef enum { + /* Found in $MFT/$DATA. */ + magic_FILE = const_cpu_to_le32(0x454c4946), /* Mft entry. */ + magic_INDX = const_cpu_to_le32(0x58444e49), /* Index buffer. */ + magic_HOLE = const_cpu_to_le32(0x454c4f48), /* ? (NTFS 3.0+?) */ + + /* Found in $LogFile/$DATA. */ + magic_RSTR = const_cpu_to_le32(0x52545352), /* Restart page. */ + magic_RCRD = const_cpu_to_le32(0x44524352), /* Log record page. */ + + /* Found in $LogFile/$DATA. (May be found in $MFT/$DATA, also?) */ + magic_CHKD = const_cpu_to_le32(0x444b4843), /* Modified by chkdsk. */ + + /* Found in all ntfs record containing records. */ + magic_BAAD = const_cpu_to_le32(0x44414142), /* Failed multi sector + transfer was detected. */ + + /* + * Found in $LogFile/$DATA when a page is full or 0xff bytes and is + * thus not initialized. User has to initialize the page before using + * it. + */ + magic_empty = const_cpu_to_le32(0xffffffff),/* Record is empty and has + to be initialized before + it can be used. */ +} NTFS_RECORD_TYPES; + +/* + * Generic magic comparison macros. Finally found a use for the ## preprocessor + * operator! (-8 + */ +#define ntfs_is_magic(x, m) ( (u32)(x) == (u32)magic_##m ) +#define ntfs_is_magicp(p, m) ( *(u32*)(p) == (u32)magic_##m ) + +/* + * Specialised magic comparison macros for the NTFS_RECORD_TYPES defined above. + */ +#define ntfs_is_file_record(x) ( ntfs_is_magic (x, FILE) ) +#define ntfs_is_file_recordp(p) ( ntfs_is_magicp(p, FILE) ) +#define ntfs_is_mft_record(x) ( ntfs_is_file_record(x) ) +#define ntfs_is_mft_recordp(p) ( ntfs_is_file_recordp(p) ) +#define ntfs_is_indx_record(x) ( ntfs_is_magic (x, INDX) ) +#define ntfs_is_indx_recordp(p) ( ntfs_is_magicp(p, INDX) ) +#define ntfs_is_hole_record(x) ( ntfs_is_magic (x, HOLE) ) +#define ntfs_is_hole_recordp(p) ( ntfs_is_magicp(p, HOLE) ) + +#define ntfs_is_rstr_record(x) ( ntfs_is_magic (x, RSTR) ) +#define ntfs_is_rstr_recordp(p) ( ntfs_is_magicp(p, RSTR) ) +#define ntfs_is_rcrd_record(x) ( ntfs_is_magic (x, RCRD) ) +#define ntfs_is_rcrd_recordp(p) ( ntfs_is_magicp(p, RCRD) ) + +#define ntfs_is_chkd_record(x) ( ntfs_is_magic (x, CHKD) ) +#define ntfs_is_chkd_recordp(p) ( ntfs_is_magicp(p, CHKD) ) + +#define ntfs_is_baad_record(x) ( ntfs_is_magic (x, BAAD) ) +#define ntfs_is_baad_recordp(p) ( ntfs_is_magicp(p, BAAD) ) + +#define ntfs_is_empty_record(x) ( ntfs_is_magic (x, empty) ) +#define ntfs_is_empty_recordp(p) ( ntfs_is_magicp(p, empty) ) + + +#define NTFS_BLOCK_SIZE 512 +#define NTFS_BLOCK_SIZE_BITS 9 + +/** + * struct NTFS_RECORD - + * + * The Update Sequence Array (usa) is an array of the u16 values which belong + * to the end of each sector protected by the update sequence record in which + * this array is contained. Note that the first entry is the Update Sequence + * Number (usn), a cyclic counter of how many times the protected record has + * been written to disk. The values 0 and -1 (ie. 0xffff) are not used. All + * last u16's of each sector have to be equal to the usn (during reading) or + * are set to it (during writing). If they are not, an incomplete multi sector + * transfer has occurred when the data was written. + * The maximum size for the update sequence array is fixed to: + * maximum size = usa_ofs + (usa_count * 2) = 510 bytes + * The 510 bytes comes from the fact that the last u16 in the array has to + * (obviously) finish before the last u16 of the first 512-byte sector. + * This formula can be used as a consistency check in that usa_ofs + + * (usa_count * 2) has to be less than or equal to 510. + */ +typedef struct { + NTFS_RECORD_TYPES magic;/* A four-byte magic identifying the + record type and/or status. */ + u16 usa_ofs; /* Offset to the Update Sequence Array (usa) + from the start of the ntfs record. */ + u16 usa_count; /* Number of u16 sized entries in the usa + including the Update Sequence Number (usn), + thus the number of fixups is the usa_count + minus 1. */ +} __attribute__((__packed__)) NTFS_RECORD; + +/** + * enum NTFS_SYSTEM_FILES - System files mft record numbers. + * + * All these files are always marked as used in the bitmap attribute of the + * mft; presumably in order to avoid accidental allocation for random other + * mft records. Also, the sequence number for each of the system files is + * always equal to their mft record number and it is never modified. + */ +typedef enum { + FILE_MFT = 0, /* Master file table (mft). Data attribute + contains the entries and bitmap attribute + records which ones are in use (bit==1). */ + FILE_MFTMirr = 1, /* Mft mirror: copy of first four mft records + in data attribute. If cluster size > 4kiB, + copy of first N mft records, with + N = cluster_size / mft_record_size. */ + FILE_LogFile = 2, /* Journalling log in data attribute. */ + FILE_Volume = 3, /* Volume name attribute and volume information + attribute (flags and ntfs version). Windows + refers to this file as volume DASD (Direct + Access Storage Device). */ + FILE_AttrDef = 4, /* Array of attribute definitions in data + attribute. */ + FILE_root = 5, /* Root directory. */ + FILE_Bitmap = 6, /* Allocation bitmap of all clusters (lcns) in + data attribute. */ + FILE_Boot = 7, /* Boot sector (always at cluster 0) in data + attribute. */ + FILE_BadClus = 8, /* Contains all bad clusters in the non-resident + data attribute. */ + FILE_Secure = 9, /* Shared security descriptors in data attribute + and two indexes into the descriptors. + Appeared in Windows 2000. Before that, this + file was named $Quota but was unused. */ + FILE_UpCase = 10, /* Uppercase equivalents of all 65536 Unicode + characters in data attribute. */ + FILE_Extend = 11, /* Directory containing other system files (eg. + $ObjId, $Quota, $Reparse and $UsnJrnl). This + is new to NTFS3.0. */ + FILE_reserved12 = 12, /* Reserved for future use (records 12-15). */ + FILE_reserved13 = 13, + FILE_reserved14 = 14, + FILE_mft_data = 15, /* Reserved for first extent of $MFT:$DATA */ + FILE_first_user = 16, /* First user file, used as test limit for + whether to allow opening a file or not. */ +} NTFS_SYSTEM_FILES; + +/** + * enum MFT_RECORD_FLAGS - + * + * These are the so far known MFT_RECORD_* flags (16-bit) which contain + * information about the mft record in which they are present. + * + * MFT_RECORD_IS_4 exists on all $Extend sub-files. + * It seems that it marks it is a metadata file with MFT record >24, however, + * it is unknown if it is limited to metadata files only. + * + * MFT_RECORD_IS_VIEW_INDEX exists on every metafile with a non directory + * index, that means an INDEX_ROOT and an INDEX_ALLOCATION with a name other + * than "$I30". It is unknown if it is limited to metadata files only. + */ +typedef enum { + MFT_RECORD_IN_USE = const_cpu_to_le16(0x0001), + MFT_RECORD_IS_DIRECTORY = const_cpu_to_le16(0x0002), + MFT_RECORD_IS_4 = const_cpu_to_le16(0x0004), + MFT_RECORD_IS_VIEW_INDEX = const_cpu_to_le16(0x0008), + MFT_REC_SPACE_FILLER = 0xffff, /* Just to make flags + 16-bit. */ +} __attribute__((__packed__)) MFT_RECORD_FLAGS; + +/* + * mft references (aka file references or file record segment references) are + * used whenever a structure needs to refer to a record in the mft. + * + * A reference consists of a 48-bit index into the mft and a 16-bit sequence + * number used to detect stale references. + * + * For error reporting purposes we treat the 48-bit index as a signed quantity. + * + * The sequence number is a circular counter (skipping 0) describing how many + * times the referenced mft record has been (re)used. This has to match the + * sequence number of the mft record being referenced, otherwise the reference + * is considered stale and removed (FIXME: only ntfsck or the driver itself?). + * + * If the sequence number is zero it is assumed that no sequence number + * consistency checking should be performed. + * + * FIXME: Since inodes are 32-bit as of now, the driver needs to always check + * for high_part being 0 and if not either BUG(), cause a panic() or handle + * the situation in some other way. This shouldn't be a problem as a volume has + * to become HUGE in order to need more than 32-bits worth of mft records. + * Assuming the standard mft record size of 1kb only the records (never mind + * the non-resident attributes, etc.) would require 4Tb of space on their own + * for the first 32 bits worth of records. This is only if some strange person + * doesn't decide to foul play and make the mft sparse which would be a really + * horrible thing to do as it would trash our current driver implementation. )-: + * Do I hear screams "we want 64-bit inodes!" ?!? (-; + * + * FIXME: The mft zone is defined as the first 12% of the volume. This space is + * reserved so that the mft can grow contiguously and hence doesn't become + * fragmented. Volume free space includes the empty part of the mft zone and + * when the volume's free 88% are used up, the mft zone is shrunk by a factor + * of 2, thus making more space available for more files/data. This process is + * repeated every time there is no more free space except for the mft zone until + * there really is no more free space. + */ + +/* + * Typedef the MFT_REF as a 64-bit value for easier handling. + * Also define two unpacking macros to get to the reference (MREF) and + * sequence number (MSEQNO) respectively. + * The _LE versions are to be applied on little endian MFT_REFs. + * Note: The _LE versions will return a CPU endian formatted value! + */ +#define MFT_REF_MASK_CPU 0x0000ffffffffffffULL +#define MFT_REF_MASK_LE const_cpu_to_le64(MFT_REF_MASK_CPU) + +typedef u64 MFT_REF; +typedef le64 leMFT_REF; /* a little-endian MFT_MREF */ + +#define MK_MREF(m, s) ((MFT_REF)(((MFT_REF)(s) << 48) | \ + ((MFT_REF)(m) & MFT_REF_MASK_CPU))) +#define MK_LE_MREF(m, s) const_cpu_to_le64(((MFT_REF)(((MFT_REF)(s) << 48) | \ + ((MFT_REF)(m) & MFT_REF_MASK_CPU)))) + +#define MREF(x) ((u64)((x) & MFT_REF_MASK_CPU)) +#define MSEQNO(x) ((u16)(((x) >> 48) & 0xffff)) +#define MREF_LE(x) ((u64)(const_le64_to_cpu(x) & MFT_REF_MASK_CPU)) +#define MSEQNO_LE(x) ((u16)((const_le64_to_cpu(x) >> 48) & 0xffff)) + +#define IS_ERR_MREF(x) (((x) & 0x0000800000000000ULL) ? 1 : 0) +#define ERR_MREF(x) ((u64)((s64)(x))) +#define MREF_ERR(x) ((int)((s64)(x))) + +/** + * struct MFT_RECORD - An MFT record layout (NTFS 3.1+) + * + * The mft record header present at the beginning of every record in the mft. + * This is followed by a sequence of variable length attribute records which + * is terminated by an attribute of type AT_END which is a truncated attribute + * in that it only consists of the attribute type code AT_END and none of the + * other members of the attribute structure are present. + */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ + u16 usa_ofs; /* See NTFS_RECORD definition above. */ + u16 usa_count; /* See NTFS_RECORD definition above. */ + +/* 8*/ LSN lsn; /* $LogFile sequence number for this record. + Changed every time the record is modified. */ +/* 16*/ u16 sequence_number; /* Number of times this mft record has been + reused. (See description for MFT_REF + above.) NOTE: The increment (skipping zero) + is done when the file is deleted. NOTE: If + this is zero it is left zero. */ +/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of + directory entries referencing this record. + NOTE: Only used in mft base records. + NOTE: When deleting a directory entry we + check the link_count and if it is 1 we + delete the file. Otherwise we delete the + FILE_NAME_ATTR being referenced by the + directory entry from the mft record and + decrement the link_count. + FIXME: Careful with Win32 + DOS names! */ +/* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this + mft record from the start of the mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file + is deleted, the MFT_RECORD_IN_USE flag is + set to zero. */ +/* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft + record. This should be equal to the mft + record size. */ +/* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. + When it is not zero it is a mft reference + pointing to the base mft record to which + this record belongs (this is then used to + locate the attribute list attribute present + in the base record which describes this + extension record and hence might need + modification when the extension record + itself is modified, also locating the + attribute list also means finding the other + potential extents, belonging to the non-base + mft record). */ +/* 40*/ u16 next_attr_instance; /* The instance number that will be + assigned to the next attribute added to this + mft record. NOTE: Incremented each time + after it is used. NOTE: Every time the mft + record is reused this number is set to zero. + NOTE: The first instance number is always 0. + */ +/* The below fields are specific to NTFS 3.1+ (Windows XP and above): */ +/* 42*/ u16 reserved; /* Reserved/alignment. */ +/* 44*/ u32 mft_record_number; /* Number of this mft record. */ +/* sizeof() = 48 bytes */ +/* + * When (re)using the mft record, we place the update sequence array at this + * offset, i.e. before we start with the attributes. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading we obviously use the data from the ntfs record header. + */ +} __attribute__((__packed__)) MFT_RECORD; + +/** + * struct MFT_RECORD_OLD - An MFT record layout (NTFS <=3.0) + * + * This is the version without the NTFS 3.1+ specific fields. + */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */ + u16 usa_ofs; /* See NTFS_RECORD definition above. */ + u16 usa_count; /* See NTFS_RECORD definition above. */ + +/* 8*/ LSN lsn; /* $LogFile sequence number for this record. + Changed every time the record is modified. */ +/* 16*/ u16 sequence_number; /* Number of times this mft record has been + reused. (See description for MFT_REF + above.) NOTE: The increment (skipping zero) + is done when the file is deleted. NOTE: If + this is zero it is left zero. */ +/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of + directory entries referencing this record. + NOTE: Only used in mft base records. + NOTE: When deleting a directory entry we + check the link_count and if it is 1 we + delete the file. Otherwise we delete the + FILE_NAME_ATTR being referenced by the + directory entry from the mft record and + decrement the link_count. + FIXME: Careful with Win32 + DOS names! */ +/* 20*/ u16 attrs_offset; /* Byte offset to the first attribute in this + mft record from the start of the mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file + is deleted, the MFT_RECORD_IN_USE flag is + set to zero. */ +/* 24*/ u32 bytes_in_use; /* Number of bytes used in this mft record. + NOTE: Must be aligned to 8-byte boundary. */ +/* 28*/ u32 bytes_allocated; /* Number of bytes allocated for this mft + record. This should be equal to the mft + record size. */ +/* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records. + When it is not zero it is a mft reference + pointing to the base mft record to which + this record belongs (this is then used to + locate the attribute list attribute present + in the base record which describes this + extension record and hence might need + modification when the extension record + itself is modified, also locating the + attribute list also means finding the other + potential extents, belonging to the non-base + mft record). */ +/* 40*/ u16 next_attr_instance; /* The instance number that will be + assigned to the next attribute added to this + mft record. NOTE: Incremented each time + after it is used. NOTE: Every time the mft + record is reused this number is set to zero. + NOTE: The first instance number is always 0. + */ +/* sizeof() = 42 bytes */ +/* + * When (re)using the mft record, we place the update sequence array at this + * offset, i.e. before we start with the attributes. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading we obviously use the data from the ntfs record header. + */ +} __attribute__((__packed__)) MFT_RECORD_OLD; + +/** + * enum ATTR_TYPES - System defined attributes (32-bit). + * + * Each attribute type has a corresponding attribute name (Unicode string of + * maximum 64 character length) as described by the attribute definitions + * present in the data attribute of the $AttrDef system file. + * + * On NTFS 3.0 volumes the names are just as the types are named in the below + * enum exchanging AT_ for the dollar sign ($). If that isn't a revealing + * choice of symbol... (-; + */ +typedef enum { + AT_UNUSED = const_cpu_to_le32( 0), + AT_STANDARD_INFORMATION = const_cpu_to_le32( 0x10), + AT_ATTRIBUTE_LIST = const_cpu_to_le32( 0x20), + AT_FILE_NAME = const_cpu_to_le32( 0x30), + AT_OBJECT_ID = const_cpu_to_le32( 0x40), + AT_SECURITY_DESCRIPTOR = const_cpu_to_le32( 0x50), + AT_VOLUME_NAME = const_cpu_to_le32( 0x60), + AT_VOLUME_INFORMATION = const_cpu_to_le32( 0x70), + AT_DATA = const_cpu_to_le32( 0x80), + AT_INDEX_ROOT = const_cpu_to_le32( 0x90), + AT_INDEX_ALLOCATION = const_cpu_to_le32( 0xa0), + AT_BITMAP = const_cpu_to_le32( 0xb0), + AT_REPARSE_POINT = const_cpu_to_le32( 0xc0), + AT_EA_INFORMATION = const_cpu_to_le32( 0xd0), + AT_EA = const_cpu_to_le32( 0xe0), + AT_PROPERTY_SET = const_cpu_to_le32( 0xf0), + AT_LOGGED_UTILITY_STREAM = const_cpu_to_le32( 0x100), + AT_FIRST_USER_DEFINED_ATTRIBUTE = const_cpu_to_le32( 0x1000), + AT_END = const_cpu_to_le32(0xffffffff), +} ATTR_TYPES; + +/** + * enum COLLATION_RULES - The collation rules for sorting views/indexes/etc + * (32-bit). + * + * COLLATION_UNICODE_STRING - Collate Unicode strings by comparing their binary + * Unicode values, except that when a character can be uppercased, the + * upper case value collates before the lower case one. + * COLLATION_FILE_NAME - Collate file names as Unicode strings. The collation + * is done very much like COLLATION_UNICODE_STRING. In fact I have no idea + * what the difference is. Perhaps the difference is that file names + * would treat some special characters in an odd way (see + * unistr.c::ntfs_collate_names() and unistr.c::legal_ansi_char_array[] + * for what I mean but COLLATION_UNICODE_STRING would not give any special + * treatment to any characters at all, but this is speculation. + * COLLATION_NTOFS_ULONG - Sorting is done according to ascending u32 key + * values. E.g. used for $SII index in FILE_Secure, which sorts by + * security_id (u32). + * COLLATION_NTOFS_SID - Sorting is done according to ascending SID values. + * E.g. used for $O index in FILE_Extend/$Quota. + * COLLATION_NTOFS_SECURITY_HASH - Sorting is done first by ascending hash + * values and second by ascending security_id values. E.g. used for $SDH + * index in FILE_Secure. + * COLLATION_NTOFS_ULONGS - Sorting is done according to a sequence of ascending + * u32 key values. E.g. used for $O index in FILE_Extend/$ObjId, which + * sorts by object_id (16-byte), by splitting up the object_id in four + * u32 values and using them as individual keys. E.g. take the following + * two security_ids, stored as follows on disk: + * 1st: a1 61 65 b7 65 7b d4 11 9e 3d 00 e0 81 10 42 59 + * 2nd: 38 14 37 d2 d2 f3 d4 11 a5 21 c8 6b 79 b1 97 45 + * To compare them, they are split into four u32 values each, like so: + * 1st: 0xb76561a1 0x11d47b65 0xe0003d9e 0x59421081 + * 2nd: 0xd2371438 0x11d4f3d2 0x6bc821a5 0x4597b179 + * Now, it is apparent why the 2nd object_id collates after the 1st: the + * first u32 value of the 1st object_id is less than the first u32 of + * the 2nd object_id. If the first u32 values of both object_ids were + * equal then the second u32 values would be compared, etc. + */ +typedef enum { + COLLATION_BINARY = const_cpu_to_le32(0), /* Collate by binary + compare where the first byte is most + significant. */ + COLLATION_FILE_NAME = const_cpu_to_le32(1), /* Collate file names + as Unicode strings. */ + COLLATION_UNICODE_STRING = const_cpu_to_le32(2), /* Collate Unicode + strings by comparing their binary + Unicode values, except that when a + character can be uppercased, the upper + case value collates before the lower + case one. */ + COLLATION_NTOFS_ULONG = const_cpu_to_le32(16), + COLLATION_NTOFS_SID = const_cpu_to_le32(17), + COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32(18), + COLLATION_NTOFS_ULONGS = const_cpu_to_le32(19), +} COLLATION_RULES; + +/** + * enum ATTR_DEF_FLAGS - + * + * The flags (32-bit) describing attribute properties in the attribute + * definition structure. FIXME: This information is based on Regis's + * information and, according to him, it is not certain and probably + * incomplete. The INDEXABLE flag is fairly certainly correct as only the file + * name attribute has this flag set and this is the only attribute indexed in + * NT4. + */ +typedef enum { + ATTR_DEF_INDEXABLE = const_cpu_to_le32(0x02), /* Attribute can be + indexed. */ + ATTR_DEF_MULTIPLE = const_cpu_to_le32(0x04), /* Attribute type + can be present multiple times in the + mft records of an inode. */ + ATTR_DEF_NOT_ZERO = const_cpu_to_le32(0x08), /* Attribute value + must contain at least one non-zero + byte. */ + ATTR_DEF_INDEXED_UNIQUE = const_cpu_to_le32(0x10), /* Attribute must be + indexed and the attribute value must be + unique for the attribute type in all of + the mft records of an inode. */ + ATTR_DEF_NAMED_UNIQUE = const_cpu_to_le32(0x20), /* Attribute must be + named and the name must be unique for + the attribute type in all of the mft + records of an inode. */ + ATTR_DEF_RESIDENT = const_cpu_to_le32(0x40), /* Attribute must be + resident. */ + ATTR_DEF_ALWAYS_LOG = const_cpu_to_le32(0x80), /* Always log + modifications to this attribute, + regardless of whether it is resident or + non-resident. Without this, only log + modifications if the attribute is + resident. */ +} ATTR_DEF_FLAGS; + +/** + * struct ATTR_DEF - + * + * The data attribute of FILE_AttrDef contains a sequence of attribute + * definitions for the NTFS volume. With this, it is supposed to be safe for an + * older NTFS driver to mount a volume containing a newer NTFS version without + * damaging it (that's the theory. In practice it's: not damaging it too much). + * Entries are sorted by attribute type. The flags describe whether the + * attribute can be resident/non-resident and possibly other things, but the + * actual bits are unknown. + */ +typedef struct { +/*hex ofs*/ +/* 0*/ ntfschar name[0x40]; /* Unicode name of the attribute. Zero + terminated. */ +/* 80*/ ATTR_TYPES type; /* Type of the attribute. */ +/* 84*/ u32 display_rule; /* Default display rule. + FIXME: What does it mean? (AIA) */ +/* 88*/ COLLATION_RULES collation_rule; /* Default collation rule. */ +/* 8c*/ ATTR_DEF_FLAGS flags; /* Flags describing the attribute. */ +/* 90*/ s64 min_size; /* Optional minimum attribute size. */ +/* 98*/ s64 max_size; /* Maximum size of attribute. */ +/* sizeof() = 0xa0 or 160 bytes */ +} __attribute__((__packed__)) ATTR_DEF; + +/** + * enum ATTR_FLAGS - Attribute flags (16-bit). + */ +typedef enum { + ATTR_IS_COMPRESSED = const_cpu_to_le16(0x0001), + ATTR_COMPRESSION_MASK = const_cpu_to_le16(0x00ff), /* Compression + method mask. Also, first + illegal value. */ + ATTR_IS_ENCRYPTED = const_cpu_to_le16(0x4000), + ATTR_IS_SPARSE = const_cpu_to_le16(0x8000), +} __attribute__((__packed__)) ATTR_FLAGS; + +/* + * Attribute compression. + * + * Only the data attribute is ever compressed in the current ntfs driver in + * Windows. Further, compression is only applied when the data attribute is + * non-resident. Finally, to use compression, the maximum allowed cluster size + * on a volume is 4kib. + * + * The compression method is based on independently compressing blocks of X + * clusters, where X is determined from the compression_unit value found in the + * non-resident attribute record header (more precisely: X = 2^compression_unit + * clusters). On Windows NT/2k, X always is 16 clusters (compression_unit = 4). + * + * There are three different cases of how a compression block of X clusters + * can be stored: + * + * 1) The data in the block is all zero (a sparse block): + * This is stored as a sparse block in the runlist, i.e. the runlist + * entry has length = X and lcn = -1. The mapping pairs array actually + * uses a delta_lcn value length of 0, i.e. delta_lcn is not present at + * all, which is then interpreted by the driver as lcn = -1. + * NOTE: Even uncompressed files can be sparse on NTFS 3.0 volumes, then + * the same principles apply as above, except that the length is not + * restricted to being any particular value. + * + * 2) The data in the block is not compressed: + * This happens when compression doesn't reduce the size of the block + * in clusters. I.e. if compression has a small effect so that the + * compressed data still occupies X clusters, then the uncompressed data + * is stored in the block. + * This case is recognised by the fact that the runlist entry has + * length = X and lcn >= 0. The mapping pairs array stores this as + * normal with a run length of X and some specific delta_lcn, i.e. + * delta_lcn has to be present. + * + * 3) The data in the block is compressed: + * The common case. This case is recognised by the fact that the run + * list entry has length L < X and lcn >= 0. The mapping pairs array + * stores this as normal with a run length of X and some specific + * delta_lcn, i.e. delta_lcn has to be present. This runlist entry is + * immediately followed by a sparse entry with length = X - L and + * lcn = -1. The latter entry is to make up the vcn counting to the + * full compression block size X. + * + * In fact, life is more complicated because adjacent entries of the same type + * can be coalesced. This means that one has to keep track of the number of + * clusters handled and work on a basis of X clusters at a time being one + * block. An example: if length L > X this means that this particular runlist + * entry contains a block of length X and part of one or more blocks of length + * L - X. Another example: if length L < X, this does not necessarily mean that + * the block is compressed as it might be that the lcn changes inside the block + * and hence the following runlist entry describes the continuation of the + * potentially compressed block. The block would be compressed if the + * following runlist entry describes at least X - L sparse clusters, thus + * making up the compression block length as described in point 3 above. (Of + * course, there can be several runlist entries with small lengths so that the + * sparse entry does not follow the first data containing entry with + * length < X.) + * + * NOTE: At the end of the compressed attribute value, there most likely is not + * just the right amount of data to make up a compression block, thus this data + * is not even attempted to be compressed. It is just stored as is, unless + * the number of clusters it occupies is reduced when compressed in which case + * it is stored as a compressed compression block, complete with sparse + * clusters at the end. + */ + +/** + * enum RESIDENT_ATTR_FLAGS - Flags of resident attributes (8-bit). + */ +typedef enum { + RESIDENT_ATTR_IS_INDEXED = 0x01, /* Attribute is referenced in an index + (has implications for deleting and + modifying the attribute). */ +} __attribute__((__packed__)) RESIDENT_ATTR_FLAGS; + +/** + * struct ATTR_RECORD - Attribute record header. + * + * Always aligned to 8-byte boundary. + */ +typedef struct { +/*Ofs*/ +/* 0*/ ATTR_TYPES type; /* The (32-bit) type of the attribute. */ +/* 4*/ u32 length; /* Byte size of the resident part of the + attribute (aligned to 8-byte boundary). + Used to get to the next attribute. */ +/* 8*/ u8 non_resident; /* If 0, attribute is resident. + If 1, attribute is non-resident. */ +/* 9*/ u8 name_length; /* Unicode character size of name of attribute. + 0 if unnamed. */ +/* 10*/ u16 name_offset; /* If name_length != 0, the byte offset to the + beginning of the name from the attribute + record. Note that the name is stored as a + Unicode string. When creating, place offset + just at the end of the record header. Then, + follow with attribute value or mapping pairs + array, resident and non-resident attributes + respectively, aligning to an 8-byte + boundary. */ +/* 12*/ ATTR_FLAGS flags; /* Flags describing the attribute. */ +/* 14*/ u16 instance; /* The instance of this attribute record. This + number is unique within this mft record (see + MFT_RECORD/next_attribute_instance notes + above for more details). */ +/* 16*/ union { + /* Resident attributes. */ + struct { +/* 16 */ u32 value_length; /* Byte size of attribute value. */ +/* 20 */ u16 value_offset; /* Byte offset of the attribute + value from the start of the + attribute record. When creating, + align to 8-byte boundary if we + have a name present as this might + not have a length of a multiple + of 8-bytes. */ +/* 22 */ RESIDENT_ATTR_FLAGS resident_flags; /* See above. */ +/* 23 */ s8 reservedR; /* Reserved/alignment to 8-byte + boundary. */ +/* 24 */ void *resident_end[0]; /* Use offsetof(ATTR_RECORD, + resident_end) to get size of + a resident attribute. */ + } __attribute__((__packed__)); + /* Non-resident attributes. */ + struct { +/* 16*/ VCN lowest_vcn; /* Lowest valid virtual cluster number + for this portion of the attribute value or + 0 if this is the only extent (usually the + case). - Only when an attribute list is used + does lowest_vcn != 0 ever occur. */ +/* 24*/ VCN highest_vcn; /* Highest valid vcn of this extent of + the attribute value. - Usually there is only one + portion, so this usually equals the attribute + value size in clusters minus 1. Can be -1 for + zero length files. Can be 0 for "single extent" + attributes. */ +/* 32*/ u16 mapping_pairs_offset; /* Byte offset from the + beginning of the structure to the mapping pairs + array which contains the mappings between the + vcns and the logical cluster numbers (lcns). + When creating, place this at the end of this + record header aligned to 8-byte boundary. */ +/* 34*/ u8 compression_unit; /* The compression unit expressed + as the log to the base 2 of the number of + clusters in a compression unit. 0 means not + compressed. (This effectively limits the + compression unit size to be a power of two + clusters.) WinNT4 only uses a value of 4. */ +/* 35*/ u8 reserved1[5]; /* Align to 8-byte boundary. */ +/* The sizes below are only used when lowest_vcn is zero, as otherwise it would + be difficult to keep them up-to-date.*/ +/* 40*/ s64 allocated_size; /* Byte size of disk space + allocated to hold the attribute value. Always + is a multiple of the cluster size. When a file + is compressed, this field is a multiple of the + compression block size (2^compression_unit) and + it represents the logically allocated space + rather than the actual on disk usage. For this + use the compressed_size (see below). */ +/* 48*/ s64 data_size; /* Byte size of the attribute + value. Can be larger than allocated_size if + attribute value is compressed or sparse. */ +/* 56*/ s64 initialized_size; /* Byte size of initialized + portion of the attribute value. Usually equals + data_size. */ +/* 64 */ void *non_resident_end[0]; /* Use offsetof(ATTR_RECORD, + non_resident_end) to get + size of a non resident + attribute. */ +/* sizeof(uncompressed attr) = 64*/ +/* 64*/ s64 compressed_size; /* Byte size of the attribute + value after compression. Only present when + compressed. Always is a multiple of the + cluster size. Represents the actual amount of + disk space being used on the disk. */ +/* 72 */ void *compressed_end[0]; + /* Use offsetof(ATTR_RECORD, compressed_end) to + get size of a compressed attribute. */ +/* sizeof(compressed attr) = 72*/ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +} __attribute__((__packed__)) ATTR_RECORD; + +typedef ATTR_RECORD ATTR_REC; + +/** + * enum FILE_ATTR_FLAGS - File attribute flags (32-bit). + */ +typedef enum { + /* + * These flags are only present in the STANDARD_INFORMATION attribute + * (in the field file_attributes). + */ + FILE_ATTR_READONLY = const_cpu_to_le32(0x00000001), + FILE_ATTR_HIDDEN = const_cpu_to_le32(0x00000002), + FILE_ATTR_SYSTEM = const_cpu_to_le32(0x00000004), + /* Old DOS volid. Unused in NT. = cpu_to_le32(0x00000008), */ + + FILE_ATTR_DIRECTORY = const_cpu_to_le32(0x00000010), + /* FILE_ATTR_DIRECTORY is not considered valid in NT. It is reserved + for the DOS SUBDIRECTORY flag. */ + FILE_ATTR_ARCHIVE = const_cpu_to_le32(0x00000020), + FILE_ATTR_DEVICE = const_cpu_to_le32(0x00000040), + FILE_ATTR_NORMAL = const_cpu_to_le32(0x00000080), + + FILE_ATTR_TEMPORARY = const_cpu_to_le32(0x00000100), + FILE_ATTR_SPARSE_FILE = const_cpu_to_le32(0x00000200), + FILE_ATTR_REPARSE_POINT = const_cpu_to_le32(0x00000400), + FILE_ATTR_COMPRESSED = const_cpu_to_le32(0x00000800), + + FILE_ATTR_OFFLINE = const_cpu_to_le32(0x00001000), + FILE_ATTR_NOT_CONTENT_INDEXED = const_cpu_to_le32(0x00002000), + FILE_ATTR_ENCRYPTED = const_cpu_to_le32(0x00004000), + + FILE_ATTR_VALID_FLAGS = const_cpu_to_le32(0x00007fb7), + /* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the + FILE_ATTR_DEVICE and preserves everything else. This mask + is used to obtain all flags that are valid for reading. */ + FILE_ATTR_VALID_SET_FLAGS = const_cpu_to_le32(0x000031a7), + /* FILE_ATTR_VALID_SET_FLAGS masks out the old DOS VolId, the + FILE_ATTR_DEVICE, FILE_ATTR_DIRECTORY, FILE_ATTR_SPARSE_FILE, + FILE_ATTR_REPARSE_POINT, FILE_ATRE_COMPRESSED and FILE_ATTR_ENCRYPTED + and preserves the rest. This mask is used to to obtain all flags that + are valid for setting. */ + + /** + * FILE_ATTR_I30_INDEX_PRESENT - Is it a directory? + * + * This is a copy of the MFT_RECORD_IS_DIRECTORY bit from the mft + * record, telling us whether this is a directory or not, i.e. whether + * it has an index root attribute named "$I30" or not. + * + * This flag is only present in the FILE_NAME attribute (in the + * file_attributes field). + */ + FILE_ATTR_I30_INDEX_PRESENT = const_cpu_to_le32(0x10000000), + + /** + * FILE_ATTR_VIEW_INDEX_PRESENT - Does have a non-directory index? + * + * This is a copy of the MFT_RECORD_IS_VIEW_INDEX bit from the mft + * record, telling us whether this file has a view index present (eg. + * object id index, quota index, one of the security indexes and the + * reparse points index). + * + * This flag is only present in the $STANDARD_INFORMATION and + * $FILE_NAME attributes. + */ + FILE_ATTR_VIEW_INDEX_PRESENT = const_cpu_to_le32(0x20000000), +} __attribute__((__packed__)) FILE_ATTR_FLAGS; + +/* + * NOTE on times in NTFS: All times are in MS standard time format, i.e. they + * are the number of 100-nanosecond intervals since 1st January 1601, 00:00:00 + * universal coordinated time (UTC). (In Linux time starts 1st January 1970, + * 00:00:00 UTC and is stored as the number of 1-second intervals since then.) + */ + +/** + * struct STANDARD_INFORMATION - Attribute: Standard information (0x10). + * + * NOTE: Always resident. + * NOTE: Present in all base file records on a volume. + * NOTE: There is conflicting information about the meaning of each of the time + * fields but the meaning as defined below has been verified to be + * correct by practical experimentation on Windows NT4 SP6a and is hence + * assumed to be the one and only correct interpretation. + */ +typedef struct { +/*Ofs*/ +/* 0*/ s64 creation_time; /* Time file was created. Updated when + a filename is changed(?). */ +/* 8*/ s64 last_data_change_time; /* Time the data attribute was last + modified. */ +/* 16*/ s64 last_mft_change_time; /* Time this mft record was last + modified. */ +/* 24*/ s64 last_access_time; /* Approximate time when the file was + last accessed (obviously this is not + updated on read-only volumes). In + Windows this is only updated when + accessed if some time delta has + passed since the last update. Also, + last access times updates can be + disabled altogether for speed. */ +/* 32*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ +/* 36*/ union { + /* NTFS 1.2 (and previous, presumably) */ + struct { + /* 36 */ u8 reserved12[12]; /* Reserved/alignment to 8-byte + boundary. */ + /* 48 */ void *v1_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); +/* sizeof() = 48 bytes */ + /* NTFS 3.0 */ + struct { +/* + * If a volume has been upgraded from a previous NTFS version, then these + * fields are present only if the file has been accessed since the upgrade. + * Recognize the difference by comparing the length of the resident attribute + * value. If it is 48, then the following fields are missing. If it is 72 then + * the fields are present. Maybe just check like this: + * if (resident.ValueLength < sizeof(STANDARD_INFORMATION)) { + * Assume NTFS 1.2- format. + * If (volume version is 3.0+) + * Upgrade attribute to NTFS 3.0 format. + * else + * Use NTFS 1.2- format for access. + * } else + * Use NTFS 3.0 format for access. + * Only problem is that it might be legal to set the length of the value to + * arbitrarily large values thus spoiling this check. - But chkdsk probably + * views that as a corruption, assuming that it behaves like this for all + * attributes. + */ + /* 36*/ u32 maximum_versions; /* Maximum allowed versions for + file. Zero if version numbering is disabled. */ + /* 40*/ u32 version_number; /* This file's version (if any). + Set to zero if maximum_versions is zero. */ + /* 44*/ u32 class_id; /* Class id from bidirectional + class id index (?). */ + /* 48*/ u32 owner_id; /* Owner_id of the user owning + the file. Translate via $Q index in FILE_Extend + /$Quota to the quota control entry for the user + owning the file. Zero if quotas are disabled. */ + /* 52*/ u32 security_id; /* Security_id for the file. + Translate via $SII index and $SDS data stream + in FILE_Secure to the security descriptor. */ + /* 56*/ u64 quota_charged; /* Byte size of the charge to + the quota for all streams of the file. Note: Is + zero if quotas are disabled. */ + /* 64*/ u64 usn; /* Last update sequence number + of the file. This is a direct index into the + change (aka usn) journal file. It is zero if + the usn journal is disabled. + NOTE: To disable the journal need to delete + the journal file itself and to then walk the + whole mft and set all Usn entries in all mft + records to zero! (This can take a while!) + The journal is FILE_Extend/$UsnJrnl. Win2k + will recreate the journal and initiate + logging if necessary when mounting the + partition. This, in contrast to disabling the + journal is a very fast process, so the user + won't even notice it. */ + /* 72*/ void *v3_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* sizeof() = 72 bytes (NTFS 3.0) */ +} __attribute__((__packed__)) STANDARD_INFORMATION; + +/** + * struct ATTR_LIST_ENTRY - Attribute: Attribute list (0x20). + * + * - Can be either resident or non-resident. + * - Value consists of a sequence of variable length, 8-byte aligned, + * ATTR_LIST_ENTRY records. + * - The attribute list attribute contains one entry for each attribute of + * the file in which the list is located, except for the list attribute + * itself. The list is sorted: first by attribute type, second by attribute + * name (if present), third by instance number. The extents of one + * non-resident attribute (if present) immediately follow after the initial + * extent. They are ordered by lowest_vcn and have their instance set to zero. + * It is not allowed to have two attributes with all sorting keys equal. + * - Further restrictions: + * - If not resident, the vcn to lcn mapping array has to fit inside the + * base mft record. + * - The attribute list attribute value has a maximum size of 256kb. This + * is imposed by the Windows cache manager. + * - Attribute lists are only used when the attributes of mft record do not + * fit inside the mft record despite all attributes (that can be made + * non-resident) having been made non-resident. This can happen e.g. when: + * - File has a large number of hard links (lots of file name + * attributes present). + * - The mapping pairs array of some non-resident attribute becomes so + * large due to fragmentation that it overflows the mft record. + * - The security descriptor is very complex (not applicable to + * NTFS 3.0 volumes). + * - There are many named streams. + */ +typedef struct { +/*Ofs*/ +/* 0*/ ATTR_TYPES type; /* Type of referenced attribute. */ +/* 4*/ u16 length; /* Byte size of this entry. */ +/* 6*/ u8 name_length; /* Size in Unicode chars of the name of the + attribute or 0 if unnamed. */ +/* 7*/ u8 name_offset; /* Byte offset to beginning of attribute name + (always set this to where the name would + start even if unnamed). */ +/* 8*/ VCN lowest_vcn; /* Lowest virtual cluster number of this portion + of the attribute value. This is usually 0. It + is non-zero for the case where one attribute + does not fit into one mft record and thus + several mft records are allocated to hold + this attribute. In the latter case, each mft + record holds one extent of the attribute and + there is one attribute list entry for each + extent. NOTE: This is DEFINITELY a signed + value! The windows driver uses cmp, followed + by jg when comparing this, thus it treats it + as signed. */ +/* 16*/ MFT_REF mft_reference; /* The reference of the mft record holding + the ATTR_RECORD for this portion of the + attribute value. */ +/* 24*/ u16 instance; /* If lowest_vcn = 0, the instance of the + attribute being referenced; otherwise 0. */ +/* 26*/ ntfschar name[0]; /* Use when creating only. When reading use + name_offset to determine the location of the + name. */ +/* sizeof() = 26 + (attribute_name_length * 2) bytes */ +} __attribute__((__packed__)) ATTR_LIST_ENTRY; + +/* + * The maximum allowed length for a file name. + */ +#define NTFS_MAX_NAME_LEN 255 + +/** + * enum FILE_NAME_TYPE_FLAGS - Possible namespaces for filenames in ntfs. + * (8-bit). + */ +typedef enum { + FILE_NAME_POSIX = 0x00, + /* This is the largest namespace. It is case sensitive and + allows all Unicode characters except for: '\0' and '/'. + Beware that in WinNT/2k files which eg have the same name + except for their case will not be distinguished by the + standard utilities and thus a "del filename" will delete + both "filename" and "fileName" without warning. */ + FILE_NAME_WIN32 = 0x01, + /* The standard WinNT/2k NTFS long filenames. Case insensitive. + All Unicode chars except: '\0', '"', '*', '/', ':', '<', + '>', '?', '\' and '|'. Further, names cannot end with a '.' + or a space. */ + FILE_NAME_DOS = 0x02, + /* The standard DOS filenames (8.3 format). Uppercase only. + All 8-bit characters greater space, except: '"', '*', '+', + ',', '/', ':', ';', '<', '=', '>', '?' and '\'. */ + FILE_NAME_WIN32_AND_DOS = 0x03, + /* 3 means that both the Win32 and the DOS filenames are + identical and hence have been saved in this single filename + record. */ +} __attribute__((__packed__)) FILE_NAME_TYPE_FLAGS; + +/** + * struct FILE_NAME_ATTR - Attribute: Filename (0x30). + * + * NOTE: Always resident. + * NOTE: All fields, except the parent_directory, are only updated when the + * filename is changed. Until then, they just become out of sync with + * reality and the more up to date values are present in the standard + * information attribute. + * NOTE: There is conflicting information about the meaning of each of the time + * fields but the meaning as defined below has been verified to be + * correct by practical experimentation on Windows NT4 SP6a and is hence + * assumed to be the one and only correct interpretation. + */ +typedef struct { +/*hex ofs*/ +/* 0*/ MFT_REF parent_directory; /* Directory this filename is + referenced from. */ +/* 8*/ s64 creation_time; /* Time file was created. */ +/* 10*/ s64 last_data_change_time; /* Time the data attribute was last + modified. */ +/* 18*/ s64 last_mft_change_time; /* Time this mft record was last + modified. */ +/* 20*/ s64 last_access_time; /* Last time this mft record was + accessed. */ +/* 28*/ s64 allocated_size; /* Byte size of on-disk allocated space + for the data attribute. So for + normal $DATA, this is the + allocated_size from the unnamed + $DATA attribute and for compressed + and/or sparse $DATA, this is the + compressed_size from the unnamed + $DATA attribute. NOTE: This is a + multiple of the cluster size. */ +/* 30*/ s64 data_size; /* Byte size of actual data in data + attribute. */ +/* 38*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */ +/* 3c*/ union { + /* 3c*/ struct { + /* 3c*/ u16 packed_ea_size; /* Size of the buffer needed to + pack the extended attributes + (EAs), if such are present.*/ + /* 3e*/ u16 reserved; /* Reserved for alignment. */ + } __attribute__((__packed__)); + /* 3c*/ u32 reparse_point_tag; /* Type of reparse point, + present only in reparse + points and only if there are + no EAs. */ + } __attribute__((__packed__)); +/* 40*/ u8 file_name_length; /* Length of file name in + (Unicode) characters. */ +/* 41*/ FILE_NAME_TYPE_FLAGS file_name_type; /* Namespace of the file name.*/ +/* 42*/ ntfschar file_name[0]; /* File name in Unicode. */ +} __attribute__((__packed__)) FILE_NAME_ATTR; + +/** + * struct GUID - GUID structures store globally unique identifiers (GUID). + * + * A GUID is a 128-bit value consisting of one group of eight hexadecimal + * digits, followed by three groups of four hexadecimal digits each, followed + * by one group of twelve hexadecimal digits. GUIDs are Microsoft's + * implementation of the distributed computing environment (DCE) universally + * unique identifier (UUID). + * + * Example of a GUID: + * 1F010768-5A73-BC91-0010-A52216A7227B + */ +typedef struct { + u32 data1; /* The first eight hexadecimal digits of the GUID. */ + u16 data2; /* The first group of four hexadecimal digits. */ + u16 data3; /* The second group of four hexadecimal digits. */ + u8 data4[8]; /* The first two bytes are the third group of four + hexadecimal digits. The remaining six bytes are the + final 12 hexadecimal digits. */ +} __attribute__((__packed__)) GUID; + +/** + * struct OBJ_ID_INDEX_DATA - FILE_Extend/$ObjId contains an index named $O. + * + * This index contains all object_ids present on the volume as the index keys + * and the corresponding mft_record numbers as the index entry data parts. + * + * The data part (defined below) also contains three other object_ids: + * birth_volume_id - object_id of FILE_Volume on which the file was first + * created. Optional (i.e. can be zero). + * birth_object_id - object_id of file when it was first created. Usually + * equals the object_id. Optional (i.e. can be zero). + * domain_id - Reserved (always zero). + */ +typedef struct { + MFT_REF mft_reference; /* Mft record containing the object_id in + the index entry key. */ + union { + struct { + GUID birth_volume_id; + GUID birth_object_id; + GUID domain_id; + } __attribute__((__packed__)); + u8 extended_info[48]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) OBJ_ID_INDEX_DATA; + +/** + * struct OBJECT_ID_ATTR - Attribute: Object id (NTFS 3.0+) (0x40). + * + * NOTE: Always resident. + */ +typedef struct { + GUID object_id; /* Unique id assigned to the + file.*/ + /* The following fields are optional. The attribute value size is 16 + bytes, i.e. sizeof(GUID), if these are not present at all. Note, + the entries can be present but one or more (or all) can be zero + meaning that that particular value(s) is(are) not defined. Note, + when the fields are missing here, it is well possible that they are + to be found within the $Extend/$ObjId system file indexed under the + above object_id. */ + union { + struct { + GUID birth_volume_id; /* Unique id of volume on which + the file was first created.*/ + GUID birth_object_id; /* Unique id of file when it was + first created. */ + GUID domain_id; /* Reserved, zero. */ + } __attribute__((__packed__)); + u8 extended_info[48]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) OBJECT_ID_ATTR; + +#if 0 +/** + * enum IDENTIFIER_AUTHORITIES - + * + * The pre-defined IDENTIFIER_AUTHORITIES used as SID_IDENTIFIER_AUTHORITY in + * the SID structure (see below). + */ +typedef enum { /* SID string prefix. */ + SECURITY_NULL_SID_AUTHORITY = {0, 0, 0, 0, 0, 0}, /* S-1-0 */ + SECURITY_WORLD_SID_AUTHORITY = {0, 0, 0, 0, 0, 1}, /* S-1-1 */ + SECURITY_LOCAL_SID_AUTHORITY = {0, 0, 0, 0, 0, 2}, /* S-1-2 */ + SECURITY_CREATOR_SID_AUTHORITY = {0, 0, 0, 0, 0, 3}, /* S-1-3 */ + SECURITY_NON_UNIQUE_AUTHORITY = {0, 0, 0, 0, 0, 4}, /* S-1-4 */ + SECURITY_NT_SID_AUTHORITY = {0, 0, 0, 0, 0, 5}, /* S-1-5 */ +} IDENTIFIER_AUTHORITIES; +#endif + +/** + * enum RELATIVE_IDENTIFIERS - + * + * These relative identifiers (RIDs) are used with the above identifier + * authorities to make up universal well-known SIDs. + * + * Note: The relative identifier (RID) refers to the portion of a SID, which + * identifies a user or group in relation to the authority that issued the SID. + * For example, the universal well-known SID Creator Owner ID (S-1-3-0) is + * made up of the identifier authority SECURITY_CREATOR_SID_AUTHORITY (3) and + * the relative identifier SECURITY_CREATOR_OWNER_RID (0). + */ +typedef enum { /* Identifier authority. */ + SECURITY_NULL_RID = 0, /* S-1-0 */ + SECURITY_WORLD_RID = 0, /* S-1-1 */ + SECURITY_LOCAL_RID = 0, /* S-1-2 */ + + SECURITY_CREATOR_OWNER_RID = 0, /* S-1-3 */ + SECURITY_CREATOR_GROUP_RID = 1, /* S-1-3 */ + + SECURITY_CREATOR_OWNER_SERVER_RID = 2, /* S-1-3 */ + SECURITY_CREATOR_GROUP_SERVER_RID = 3, /* S-1-3 */ + + SECURITY_DIALUP_RID = 1, + SECURITY_NETWORK_RID = 2, + SECURITY_BATCH_RID = 3, + SECURITY_INTERACTIVE_RID = 4, + SECURITY_SERVICE_RID = 6, + SECURITY_ANONYMOUS_LOGON_RID = 7, + SECURITY_PROXY_RID = 8, + SECURITY_ENTERPRISE_CONTROLLERS_RID=9, + SECURITY_SERVER_LOGON_RID = 9, + SECURITY_PRINCIPAL_SELF_RID = 0xa, + SECURITY_AUTHENTICATED_USER_RID = 0xb, + SECURITY_RESTRICTED_CODE_RID = 0xc, + SECURITY_TERMINAL_SERVER_RID = 0xd, + + SECURITY_LOGON_IDS_RID = 5, + SECURITY_LOGON_IDS_RID_COUNT = 3, + + SECURITY_LOCAL_SYSTEM_RID = 0x12, + + SECURITY_NT_NON_UNIQUE = 0x15, + + SECURITY_BUILTIN_DOMAIN_RID = 0x20, + + /* + * Well-known domain relative sub-authority values (RIDs). + */ + + /* Users. */ + DOMAIN_USER_RID_ADMIN = 0x1f4, + DOMAIN_USER_RID_GUEST = 0x1f5, + DOMAIN_USER_RID_KRBTGT = 0x1f6, + + /* Groups. */ + DOMAIN_GROUP_RID_ADMINS = 0x200, + DOMAIN_GROUP_RID_USERS = 0x201, + DOMAIN_GROUP_RID_GUESTS = 0x202, + DOMAIN_GROUP_RID_COMPUTERS = 0x203, + DOMAIN_GROUP_RID_CONTROLLERS = 0x204, + DOMAIN_GROUP_RID_CERT_ADMINS = 0x205, + DOMAIN_GROUP_RID_SCHEMA_ADMINS = 0x206, + DOMAIN_GROUP_RID_ENTERPRISE_ADMINS= 0x207, + DOMAIN_GROUP_RID_POLICY_ADMINS = 0x208, + + /* Aliases. */ + DOMAIN_ALIAS_RID_ADMINS = 0x220, + DOMAIN_ALIAS_RID_USERS = 0x221, + DOMAIN_ALIAS_RID_GUESTS = 0x222, + DOMAIN_ALIAS_RID_POWER_USERS = 0x223, + + DOMAIN_ALIAS_RID_ACCOUNT_OPS = 0x224, + DOMAIN_ALIAS_RID_SYSTEM_OPS = 0x225, + DOMAIN_ALIAS_RID_PRINT_OPS = 0x226, + DOMAIN_ALIAS_RID_BACKUP_OPS = 0x227, + + DOMAIN_ALIAS_RID_REPLICATOR = 0x228, + DOMAIN_ALIAS_RID_RAS_SERVERS = 0x229, + DOMAIN_ALIAS_RID_PREW2KCOMPACCESS = 0x22a, +} RELATIVE_IDENTIFIERS; + +/* + * The universal well-known SIDs: + * + * NULL_SID S-1-0-0 + * WORLD_SID S-1-1-0 + * LOCAL_SID S-1-2-0 + * CREATOR_OWNER_SID S-1-3-0 + * CREATOR_GROUP_SID S-1-3-1 + * CREATOR_OWNER_SERVER_SID S-1-3-2 + * CREATOR_GROUP_SERVER_SID S-1-3-3 + * + * (Non-unique IDs) S-1-4 + * + * NT well-known SIDs: + * + * NT_AUTHORITY_SID S-1-5 + * DIALUP_SID S-1-5-1 + * + * NETWORD_SID S-1-5-2 + * BATCH_SID S-1-5-3 + * INTERACTIVE_SID S-1-5-4 + * SERVICE_SID S-1-5-6 + * ANONYMOUS_LOGON_SID S-1-5-7 (aka null logon session) + * PROXY_SID S-1-5-8 + * SERVER_LOGON_SID S-1-5-9 (aka domain controller account) + * SELF_SID S-1-5-10 (self RID) + * AUTHENTICATED_USER_SID S-1-5-11 + * RESTRICTED_CODE_SID S-1-5-12 (running restricted code) + * TERMINAL_SERVER_SID S-1-5-13 (running on terminal server) + * + * (Logon IDs) S-1-5-5-X-Y + * + * (NT non-unique IDs) S-1-5-0x15-... + * + * (Built-in domain) S-1-5-0x20 + */ + +/** + * union SID_IDENTIFIER_AUTHORITY - A 48-bit value used in the SID structure + * + * NOTE: This is stored as a big endian number. + */ +typedef union { + struct { + u16 high_part; /* High 16-bits. */ + u32 low_part; /* Low 32-bits. */ + } __attribute__((__packed__)); + u8 value[6]; /* Value as individual bytes. */ +} __attribute__((__packed__)) SID_IDENTIFIER_AUTHORITY; + +/** + * struct SID - + * + * The SID structure is a variable-length structure used to uniquely identify + * users or groups. SID stands for security identifier. + * + * The standard textual representation of the SID is of the form: + * S-R-I-S-S... + * Where: + * - The first "S" is the literal character 'S' identifying the following + * digits as a SID. + * - R is the revision level of the SID expressed as a sequence of digits + * in decimal. + * - I is the 48-bit identifier_authority, expressed as digits in decimal, + * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. + * - S... is one or more sub_authority values, expressed as digits in + * decimal. + * + * Example SID; the domain-relative SID of the local Administrators group on + * Windows NT/2k: + * S-1-5-32-544 + * This translates to a SID with: + * revision = 1, + * sub_authority_count = 2, + * identifier_authority = {0,0,0,0,0,5}, // SECURITY_NT_AUTHORITY + * sub_authority[0] = 32, // SECURITY_BUILTIN_DOMAIN_RID + * sub_authority[1] = 544 // DOMAIN_ALIAS_RID_ADMINS + */ +typedef struct { + u8 revision; + u8 sub_authority_count; + SID_IDENTIFIER_AUTHORITY identifier_authority; + u32 sub_authority[1]; /* At least one sub_authority. */ +} __attribute__((__packed__)) SID; + +/** + * enum SID_CONSTANTS - Current constants for SIDs. + */ +typedef enum { + SID_REVISION = 1, /* Current revision level. */ + SID_MAX_SUB_AUTHORITIES = 15, /* Maximum number of those. */ + SID_RECOMMENDED_SUB_AUTHORITIES = 1, /* Will change to around 6 in + a future revision. */ +} SID_CONSTANTS; + +/** + * enum ACE_TYPES - The predefined ACE types (8-bit, see below). + */ +typedef enum { + ACCESS_MIN_MS_ACE_TYPE = 0, + ACCESS_ALLOWED_ACE_TYPE = 0, + ACCESS_DENIED_ACE_TYPE = 1, + SYSTEM_AUDIT_ACE_TYPE = 2, + SYSTEM_ALARM_ACE_TYPE = 3, /* Not implemented as of Win2k. */ + ACCESS_MAX_MS_V2_ACE_TYPE = 3, + + ACCESS_ALLOWED_COMPOUND_ACE_TYPE= 4, + ACCESS_MAX_MS_V3_ACE_TYPE = 4, + + /* The following are Win2k only. */ + ACCESS_MIN_MS_OBJECT_ACE_TYPE = 5, + ACCESS_ALLOWED_OBJECT_ACE_TYPE = 5, + ACCESS_DENIED_OBJECT_ACE_TYPE = 6, + SYSTEM_AUDIT_OBJECT_ACE_TYPE = 7, + SYSTEM_ALARM_OBJECT_ACE_TYPE = 8, + ACCESS_MAX_MS_OBJECT_ACE_TYPE = 8, + + ACCESS_MAX_MS_V4_ACE_TYPE = 8, + + /* This one is for WinNT&2k. */ + ACCESS_MAX_MS_ACE_TYPE = 8, +} __attribute__((__packed__)) ACE_TYPES; + +/** + * enum ACE_FLAGS - The ACE flags (8-bit) for audit and inheritance. + * + * SUCCESSFUL_ACCESS_ACE_FLAG is only used with system audit and alarm ACE + * types to indicate that a message is generated (in Windows!) for successful + * accesses. + * + * FAILED_ACCESS_ACE_FLAG is only used with system audit and alarm ACE types + * to indicate that a message is generated (in Windows!) for failed accesses. + */ +typedef enum { + /* The inheritance flags. */ + OBJECT_INHERIT_ACE = 0x01, + CONTAINER_INHERIT_ACE = 0x02, + NO_PROPAGATE_INHERIT_ACE = 0x04, + INHERIT_ONLY_ACE = 0x08, + INHERITED_ACE = 0x10, /* Win2k only. */ + VALID_INHERIT_FLAGS = 0x1f, + + /* The audit flags. */ + SUCCESSFUL_ACCESS_ACE_FLAG = 0x40, + FAILED_ACCESS_ACE_FLAG = 0x80, +} __attribute__((__packed__)) ACE_FLAGS; + +/** + * struct ACE_HEADER - + * + * An ACE is an access-control entry in an access-control list (ACL). + * An ACE defines access to an object for a specific user or group or defines + * the types of access that generate system-administration messages or alarms + * for a specific user or group. The user or group is identified by a security + * identifier (SID). + * + * Each ACE starts with an ACE_HEADER structure (aligned on 4-byte boundary), + * which specifies the type and size of the ACE. The format of the subsequent + * data depends on the ACE type. + */ +typedef struct { + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ +} __attribute__((__packed__)) ACE_HEADER; + +/** + * enum ACCESS_MASK - The access mask (32-bit). + * + * Defines the access rights. + */ +typedef enum { + /* + * The specific rights (bits 0 to 15). Depend on the type of the + * object being secured by the ACE. + */ + + /* Specific rights for files and directories are as follows: */ + + /* Right to read data from the file. (FILE) */ + FILE_READ_DATA = const_cpu_to_le32(0x00000001), + /* Right to list contents of a directory. (DIRECTORY) */ + FILE_LIST_DIRECTORY = const_cpu_to_le32(0x00000001), + + /* Right to write data to the file. (FILE) */ + FILE_WRITE_DATA = const_cpu_to_le32(0x00000002), + /* Right to create a file in the directory. (DIRECTORY) */ + FILE_ADD_FILE = const_cpu_to_le32(0x00000002), + + /* Right to append data to the file. (FILE) */ + FILE_APPEND_DATA = const_cpu_to_le32(0x00000004), + /* Right to create a subdirectory. (DIRECTORY) */ + FILE_ADD_SUBDIRECTORY = const_cpu_to_le32(0x00000004), + + /* Right to read extended attributes. (FILE/DIRECTORY) */ + FILE_READ_EA = const_cpu_to_le32(0x00000008), + + /* Right to write extended attributes. (FILE/DIRECTORY) */ + FILE_WRITE_EA = const_cpu_to_le32(0x00000010), + + /* Right to execute a file. (FILE) */ + FILE_EXECUTE = const_cpu_to_le32(0x00000020), + /* Right to traverse the directory. (DIRECTORY) */ + FILE_TRAVERSE = const_cpu_to_le32(0x00000020), + + /* + * Right to delete a directory and all the files it contains (its + * children), even if the files are read-only. (DIRECTORY) + */ + FILE_DELETE_CHILD = const_cpu_to_le32(0x00000040), + + /* Right to read file attributes. (FILE/DIRECTORY) */ + FILE_READ_ATTRIBUTES = const_cpu_to_le32(0x00000080), + + /* Right to change file attributes. (FILE/DIRECTORY) */ + FILE_WRITE_ATTRIBUTES = const_cpu_to_le32(0x00000100), + + /* + * The standard rights (bits 16 to 23). Are independent of the type of + * object being secured. + */ + + /* Right to delete the object. */ + DELETE = const_cpu_to_le32(0x00010000), + + /* + * Right to read the information in the object's security descriptor, + * not including the information in the SACL. I.e. right to read the + * security descriptor and owner. + */ + READ_CONTROL = const_cpu_to_le32(0x00020000), + + /* Right to modify the DACL in the object's security descriptor. */ + WRITE_DAC = const_cpu_to_le32(0x00040000), + + /* Right to change the owner in the object's security descriptor. */ + WRITE_OWNER = const_cpu_to_le32(0x00080000), + + /* + * Right to use the object for synchronization. Enables a process to + * wait until the object is in the signalled state. Some object types + * do not support this access right. + */ + SYNCHRONIZE = const_cpu_to_le32(0x00100000), + + /* + * The following STANDARD_RIGHTS_* are combinations of the above for + * convenience and are defined by the Win32 API. + */ + + /* These are currently defined to READ_CONTROL. */ + STANDARD_RIGHTS_READ = const_cpu_to_le32(0x00020000), + STANDARD_RIGHTS_WRITE = const_cpu_to_le32(0x00020000), + STANDARD_RIGHTS_EXECUTE = const_cpu_to_le32(0x00020000), + + /* Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access. */ + STANDARD_RIGHTS_REQUIRED = const_cpu_to_le32(0x000f0000), + + /* + * Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and + * SYNCHRONIZE access. + */ + STANDARD_RIGHTS_ALL = const_cpu_to_le32(0x001f0000), + + /* + * The access system ACL and maximum allowed access types (bits 24 to + * 25, bits 26 to 27 are reserved). + */ + ACCESS_SYSTEM_SECURITY = const_cpu_to_le32(0x01000000), + MAXIMUM_ALLOWED = const_cpu_to_le32(0x02000000), + + /* + * The generic rights (bits 28 to 31). These map onto the standard and + * specific rights. + */ + + /* Read, write, and execute access. */ + GENERIC_ALL = const_cpu_to_le32(0x10000000), + + /* Execute access. */ + GENERIC_EXECUTE = const_cpu_to_le32(0x20000000), + + /* + * Write access. For files, this maps onto: + * FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | + * FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE + * For directories, the mapping has the same numerical value. See + * above for the descriptions of the rights granted. + */ + GENERIC_WRITE = const_cpu_to_le32(0x40000000), + + /* + * Read access. For files, this maps onto: + * FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_READ_EA | + * STANDARD_RIGHTS_READ | SYNCHRONIZE + * For directories, the mapping has the same numerical value. See + * above for the descriptions of the rights granted. + */ + GENERIC_READ = const_cpu_to_le32(0x80000000), +} ACCESS_MASK; + +/** + * struct GENERIC_MAPPING - + * + * The generic mapping array. Used to denote the mapping of each generic + * access right to a specific access mask. + * + * FIXME: What exactly is this and what is it for? (AIA) + */ +typedef struct { + ACCESS_MASK generic_read; + ACCESS_MASK generic_write; + ACCESS_MASK generic_execute; + ACCESS_MASK generic_all; +} __attribute__((__packed__)) GENERIC_MAPPING; + +/* + * The predefined ACE type structures are as defined below. + */ + +/** + * struct ACCESS_DENIED_ACE - + * + * ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE + */ +typedef struct { +/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ + +/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ +/* 8*/ SID sid; /* The SID associated with the ACE. */ +} __attribute__((__packed__)) ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, + SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE; + +/** + * enum OBJECT_ACE_FLAGS - The object ACE flags (32-bit). + */ +typedef enum { + ACE_OBJECT_TYPE_PRESENT = const_cpu_to_le32(1), + ACE_INHERITED_OBJECT_TYPE_PRESENT = const_cpu_to_le32(2), +} OBJECT_ACE_FLAGS; + +/** + * struct ACCESS_ALLOWED_OBJECT_ACE - + */ +typedef struct { +/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */ + ACE_TYPES type; /* Type of the ACE. */ + ACE_FLAGS flags; /* Flags describing the ACE. */ + u16 size; /* Size in bytes of the ACE. */ + +/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */ +/* 8*/ OBJECT_ACE_FLAGS object_flags; /* Flags describing the object ACE. */ +/* 12*/ GUID object_type; +/* 28*/ GUID inherited_object_type; +/* 44*/ SID sid; /* The SID associated with the ACE. */ +} __attribute__((__packed__)) ACCESS_ALLOWED_OBJECT_ACE, + ACCESS_DENIED_OBJECT_ACE, + SYSTEM_AUDIT_OBJECT_ACE, + SYSTEM_ALARM_OBJECT_ACE; + +/** + * struct ACL - An ACL is an access-control list (ACL). + * + * An ACL starts with an ACL header structure, which specifies the size of + * the ACL and the number of ACEs it contains. The ACL header is followed by + * zero or more access control entries (ACEs). The ACL as well as each ACE + * are aligned on 4-byte boundaries. + */ +typedef struct { + u8 revision; /* Revision of this ACL. */ + u8 alignment1; + u16 size; /* Allocated space in bytes for ACL. Includes this + header, the ACEs and the remaining free space. */ + u16 ace_count; /* Number of ACEs in the ACL. */ + u16 alignment2; +/* sizeof() = 8 bytes */ +} __attribute__((__packed__)) ACL; + +/** + * enum ACL_CONSTANTS - Current constants for ACLs. + */ +typedef enum { + /* Current revision. */ + ACL_REVISION = 2, + ACL_REVISION_DS = 4, + + /* History of revisions. */ + ACL_REVISION1 = 1, + MIN_ACL_REVISION = 2, + ACL_REVISION2 = 2, + ACL_REVISION3 = 3, + ACL_REVISION4 = 4, + MAX_ACL_REVISION = 4, +} ACL_CONSTANTS; + +/** + * enum SECURITY_DESCRIPTOR_CONTROL - + * + * The security descriptor control flags (16-bit). + * + * SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the + * SID pointed to by the Owner field was provided by a + * defaulting mechanism rather than explicitly provided by the + * original provider of the security descriptor. This may + * affect the treatment of the SID with respect to inheritance + * of an owner. + * + * SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the + * SID in the Group field was provided by a defaulting mechanism + * rather than explicitly provided by the original provider of + * the security descriptor. This may affect the treatment of + * the SID with respect to inheritance of a primary group. + * + * SE_DACL_PRESENT - This boolean flag, when set, indicates that the + * security descriptor contains a discretionary ACL. If this + * flag is set and the Dacl field of the SECURITY_DESCRIPTOR is + * null, then a null ACL is explicitly being specified. + * + * SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the + * ACL pointed to by the Dacl field was provided by a defaulting + * mechanism rather than explicitly provided by the original + * provider of the security descriptor. This may affect the + * treatment of the ACL with respect to inheritance of an ACL. + * This flag is ignored if the DaclPresent flag is not set. + * + * SE_SACL_PRESENT - This boolean flag, when set, indicates that the + * security descriptor contains a system ACL pointed to by the + * Sacl field. If this flag is set and the Sacl field of the + * SECURITY_DESCRIPTOR is null, then an empty (but present) + * ACL is being specified. + * + * SE_SACL_DEFAULTED - This boolean flag, when set, indicates that the + * ACL pointed to by the Sacl field was provided by a defaulting + * mechanism rather than explicitly provided by the original + * provider of the security descriptor. This may affect the + * treatment of the ACL with respect to inheritance of an ACL. + * This flag is ignored if the SaclPresent flag is not set. + * + * SE_SELF_RELATIVE - This boolean flag, when set, indicates that the + * security descriptor is in self-relative form. In this form, + * all fields of the security descriptor are contiguous in memory + * and all pointer fields are expressed as offsets from the + * beginning of the security descriptor. + */ +typedef enum { + SE_OWNER_DEFAULTED = const_cpu_to_le16(0x0001), + SE_GROUP_DEFAULTED = const_cpu_to_le16(0x0002), + SE_DACL_PRESENT = const_cpu_to_le16(0x0004), + SE_DACL_DEFAULTED = const_cpu_to_le16(0x0008), + SE_SACL_PRESENT = const_cpu_to_le16(0x0010), + SE_SACL_DEFAULTED = const_cpu_to_le16(0x0020), + SE_DACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0100), + SE_SACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0200), + SE_DACL_AUTO_INHERITED = const_cpu_to_le16(0x0400), + SE_SACL_AUTO_INHERITED = const_cpu_to_le16(0x0800), + SE_DACL_PROTECTED = const_cpu_to_le16(0x1000), + SE_SACL_PROTECTED = const_cpu_to_le16(0x2000), + SE_RM_CONTROL_VALID = const_cpu_to_le16(0x4000), + SE_SELF_RELATIVE = const_cpu_to_le16(0x8000), +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_CONTROL; + +/** + * struct SECURITY_DESCRIPTOR_RELATIVE - + * + * Self-relative security descriptor. Contains the owner and group SIDs as well + * as the sacl and dacl ACLs inside the security descriptor itself. + */ +typedef struct { + u8 revision; /* Revision level of the security descriptor. */ + u8 alignment; + SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of + the descriptor as well as the following fields. */ + u32 owner; /* Byte offset to a SID representing an object's + owner. If this is NULL, no owner SID is present in + the descriptor. */ + u32 group; /* Byte offset to a SID representing an object's + primary group. If this is NULL, no primary group + SID is present in the descriptor. */ + u32 sacl; /* Byte offset to a system ACL. Only valid, if + SE_SACL_PRESENT is set in the control field. If + SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL + is specified. */ + u32 dacl; /* Byte offset to a discretionary ACL. Only valid, if + SE_DACL_PRESENT is set in the control field. If + SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL + (unconditionally granting access) is specified. */ +/* sizeof() = 0x14 bytes */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_RELATIVE; + +/** + * struct SECURITY_DESCRIPTOR - Absolute security descriptor. + * + * Does not contain the owner and group SIDs, nor the sacl and dacl ACLs inside + * the security descriptor. Instead, it contains pointers to these structures + * in memory. Obviously, absolute security descriptors are only useful for in + * memory representations of security descriptors. + * + * On disk, a self-relative security descriptor is used. + */ +typedef struct { + u8 revision; /* Revision level of the security descriptor. */ + u8 alignment; + SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of + the descriptor as well as the following fields. */ + SID *owner; /* Points to a SID representing an object's owner. If + this is NULL, no owner SID is present in the + descriptor. */ + SID *group; /* Points to a SID representing an object's primary + group. If this is NULL, no primary group SID is + present in the descriptor. */ + ACL *sacl; /* Points to a system ACL. Only valid, if + SE_SACL_PRESENT is set in the control field. If + SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL + is specified. */ + ACL *dacl; /* Points to a discretionary ACL. Only valid, if + SE_DACL_PRESENT is set in the control field. If + SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL + (unconditionally granting access) is specified. */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR; + +/** + * enum SECURITY_DESCRIPTOR_CONSTANTS - + * + * Current constants for security descriptors. + */ +typedef enum { + /* Current revision. */ + SECURITY_DESCRIPTOR_REVISION = 1, + SECURITY_DESCRIPTOR_REVISION1 = 1, + + /* The sizes of both the absolute and relative security descriptors is + the same as pointers, at least on ia32 architecture are 32-bit. */ + SECURITY_DESCRIPTOR_MIN_LENGTH = sizeof(SECURITY_DESCRIPTOR), +} SECURITY_DESCRIPTOR_CONSTANTS; + +/* + * Attribute: Security descriptor (0x50). + * + * A standard self-relative security descriptor. + * + * NOTE: Can be resident or non-resident. + * NOTE: Not used in NTFS 3.0+, as security descriptors are stored centrally + * in FILE_Secure and the correct descriptor is found using the security_id + * from the standard information attribute. + */ +typedef SECURITY_DESCRIPTOR_RELATIVE SECURITY_DESCRIPTOR_ATTR; + +/* + * On NTFS 3.0+, all security descriptors are stored in FILE_Secure. Only one + * referenced instance of each unique security descriptor is stored. + * + * FILE_Secure contains no unnamed data attribute, i.e. it has zero length. It + * does, however, contain two indexes ($SDH and $SII) as well as a named data + * stream ($SDS). + * + * Every unique security descriptor is assigned a unique security identifier + * (security_id, not to be confused with a SID). The security_id is unique for + * the NTFS volume and is used as an index into the $SII index, which maps + * security_ids to the security descriptor's storage location within the $SDS + * data attribute. The $SII index is sorted by ascending security_id. + * + * A simple hash is computed from each security descriptor. This hash is used + * as an index into the $SDH index, which maps security descriptor hashes to + * the security descriptor's storage location within the $SDS data attribute. + * The $SDH index is sorted by security descriptor hash and is stored in a B+ + * tree. When searching $SDH (with the intent of determining whether or not a + * new security descriptor is already present in the $SDS data stream), if a + * matching hash is found, but the security descriptors do not match, the + * search in the $SDH index is continued, searching for a next matching hash. + * + * When a precise match is found, the security_id corresponding to the security + * descriptor in the $SDS attribute is read from the found $SDH index entry and + * is stored in the $STANDARD_INFORMATION attribute of the file/directory to + * which the security descriptor is being applied. The $STANDARD_INFORMATION + * attribute is present in all base mft records (i.e. in all files and + * directories). + * + * If a match is not found, the security descriptor is assigned a new unique + * security_id and is added to the $SDS data attribute. Then, entries + * referencing the this security descriptor in the $SDS data attribute are + * added to the $SDH and $SII indexes. + * + * Note: Entries are never deleted from FILE_Secure, even if nothing + * references an entry any more. + */ + +/** + * struct SECURITY_DESCRIPTOR_HEADER - + * + * This header precedes each security descriptor in the $SDS data stream. + * This is also the index entry data part of both the $SII and $SDH indexes. + */ +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ +} __attribute__((__packed__)) SECURITY_DESCRIPTOR_HEADER; + +/** + * struct SDH_INDEX_DATA - + */ +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ + u32 reserved_II; /* Padding - always unicode "II" or zero. This field + isn't counted in INDEX_ENTRY's data_length. */ +} __attribute__((__packed__)) SDH_INDEX_DATA; + +/** + * struct SII_INDEX_DATA - + */ +typedef SECURITY_DESCRIPTOR_HEADER SII_INDEX_DATA; + +/** + * struct SDS_ENTRY - + * + * The $SDS data stream contains the security descriptors, aligned on 16-byte + * boundaries, sorted by security_id in a B+ tree. Security descriptors cannot + * cross 256kib boundaries (this restriction is imposed by the Windows cache + * manager). Each security descriptor is contained in a SDS_ENTRY structure. + * Also, each security descriptor is stored twice in the $SDS stream with a + * fixed offset of 0x40000 bytes (256kib, the Windows cache manager's max size) + * between them; i.e. if a SDS_ENTRY specifies an offset of 0x51d0, then the + * the first copy of the security descriptor will be at offset 0x51d0 in the + * $SDS data stream and the second copy will be at offset 0x451d0. + */ +typedef struct { +/* 0 SECURITY_DESCRIPTOR_HEADER; -- Unfolded here as gcc doesn't like + unnamed structs. */ + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ + u64 offset; /* Byte offset of this entry in the $SDS stream. */ + u32 length; /* Size in bytes of this entry in $SDS stream. */ +/* 20*/ SECURITY_DESCRIPTOR_RELATIVE sid; /* The self-relative security + descriptor. */ +} __attribute__((__packed__)) SDS_ENTRY; + +/** + * struct SII_INDEX_KEY - The index entry key used in the $SII index. + * + * The collation type is COLLATION_NTOFS_ULONG. + */ +typedef struct { + u32 security_id; /* The security_id assigned to the descriptor. */ +} __attribute__((__packed__)) SII_INDEX_KEY; + +/** + * struct SDH_INDEX_KEY - The index entry key used in the $SDH index. + * + * The keys are sorted first by hash and then by security_id. + * The collation rule is COLLATION_NTOFS_SECURITY_HASH. + */ +typedef struct { + u32 hash; /* Hash of the security descriptor. */ + u32 security_id; /* The security_id assigned to the descriptor. */ +} __attribute__((__packed__)) SDH_INDEX_KEY; + +/** + * struct VOLUME_NAME - Attribute: Volume name (0x60). + * + * NOTE: Always resident. + * NOTE: Present only in FILE_Volume. + */ +typedef struct { + ntfschar name[0]; /* The name of the volume in Unicode. */ +} __attribute__((__packed__)) VOLUME_NAME; + +/** + * enum VOLUME_FLAGS - Possible flags for the volume (16-bit). + */ +typedef enum { + VOLUME_IS_DIRTY = const_cpu_to_le16(0x0001), + VOLUME_RESIZE_LOG_FILE = const_cpu_to_le16(0x0002), + VOLUME_UPGRADE_ON_MOUNT = const_cpu_to_le16(0x0004), + VOLUME_MOUNTED_ON_NT4 = const_cpu_to_le16(0x0008), + VOLUME_DELETE_USN_UNDERWAY = const_cpu_to_le16(0x0010), + VOLUME_REPAIR_OBJECT_ID = const_cpu_to_le16(0x0020), + VOLUME_CHKDSK_UNDERWAY = const_cpu_to_le16(0x4000), + VOLUME_MODIFIED_BY_CHKDSK = const_cpu_to_le16(0x8000), + VOLUME_FLAGS_MASK = const_cpu_to_le16(0xc03f), +} __attribute__((__packed__)) VOLUME_FLAGS; + +/** + * struct VOLUME_INFORMATION - Attribute: Volume information (0x70). + * + * NOTE: Always resident. + * NOTE: Present only in FILE_Volume. + * NOTE: Windows 2000 uses NTFS 3.0 while Windows NT4 service pack 6a uses + * NTFS 1.2. I haven't personally seen other values yet. + */ +typedef struct { + u64 reserved; /* Not used (yet?). */ + u8 major_ver; /* Major version of the ntfs format. */ + u8 minor_ver; /* Minor version of the ntfs format. */ + VOLUME_FLAGS flags; /* Bit array of VOLUME_* flags. */ +} __attribute__((__packed__)) VOLUME_INFORMATION; + +/** + * struct DATA_ATTR - Attribute: Data attribute (0x80). + * + * NOTE: Can be resident or non-resident. + * + * Data contents of a file (i.e. the unnamed stream) or of a named stream. + */ +typedef struct { + u8 data[0]; /* The file's data contents. */ +} __attribute__((__packed__)) DATA_ATTR; + +/** + * enum INDEX_HEADER_FLAGS - Index header flags (8-bit). + */ +typedef enum { + /* When index header is in an index root attribute: */ + SMALL_INDEX = 0, /* The index is small enough to fit inside the + index root attribute and there is no index + allocation attribute present. */ + LARGE_INDEX = 1, /* The index is too large to fit in the index + root attribute and/or an index allocation + attribute is present. */ + /* + * When index header is in an index block, i.e. is part of index + * allocation attribute: + */ + LEAF_NODE = 0, /* This is a leaf node, i.e. there are no more + nodes branching off it. */ + INDEX_NODE = 1, /* This node indexes other nodes, i.e. is not a + leaf node. */ + NODE_MASK = 1, /* Mask for accessing the *_NODE bits. */ +} __attribute__((__packed__)) INDEX_HEADER_FLAGS; + +/** + * struct INDEX_HEADER - + * + * This is the header for indexes, describing the INDEX_ENTRY records, which + * follow the INDEX_HEADER. Together the index header and the index entries + * make up a complete index. + * + * IMPORTANT NOTE: The offset, length and size structure members are counted + * relative to the start of the index header structure and not relative to the + * start of the index root or index allocation structures themselves. + */ +typedef struct { +/* 0*/ u32 entries_offset; /* Byte offset from the INDEX_HEADER to first + INDEX_ENTRY, aligned to 8-byte boundary. */ +/* 4*/ u32 index_length; /* Data size in byte of the INDEX_ENTRY's, + including the INDEX_HEADER, aligned to 8. */ +/* 8*/ u32 allocated_size; /* Allocated byte size of this index (block), + multiple of 8 bytes. See more below. */ + /* + For the index root attribute, the above two numbers are always + equal, as the attribute is resident and it is resized as needed. + + For the index allocation attribute, the attribute is not resident + and the allocated_size is equal to the index_block_size specified + by the corresponding INDEX_ROOT attribute minus the INDEX_BLOCK + size not counting the INDEX_HEADER part (i.e. minus -24). + */ +/* 12*/ INDEX_HEADER_FLAGS ih_flags; /* Bit field of INDEX_HEADER_FLAGS. */ +/* 13*/ u8 reserved[3]; /* Reserved/align to 8-byte boundary.*/ +/* sizeof() == 16 */ +} __attribute__((__packed__)) INDEX_HEADER; + +/** + * struct INDEX_ROOT - Attribute: Index root (0x90). + * + * NOTE: Always resident. + * + * This is followed by a sequence of index entries (INDEX_ENTRY structures) + * as described by the index header. + * + * When a directory is small enough to fit inside the index root then this + * is the only attribute describing the directory. When the directory is too + * large to fit in the index root, on the other hand, two additional attributes + * are present: an index allocation attribute, containing sub-nodes of the B+ + * directory tree (see below), and a bitmap attribute, describing which virtual + * cluster numbers (vcns) in the index allocation attribute are in use by an + * index block. + * + * NOTE: The root directory (FILE_root) contains an entry for itself. Other + * directories do not contain entries for themselves, though. + */ +typedef struct { +/* 0*/ ATTR_TYPES type; /* Type of the indexed attribute. Is + $FILE_NAME for directories, zero + for view indexes. No other values + allowed. */ +/* 4*/ COLLATION_RULES collation_rule; /* Collation rule used to sort the + index entries. If type is $FILE_NAME, + this must be COLLATION_FILE_NAME. */ +/* 8*/ u32 index_block_size; /* Size of index block in bytes (in + the index allocation attribute). */ +/* 12*/ s8 clusters_per_index_block; /* Size of index block in clusters (in + the index allocation attribute), when + an index block is >= than a cluster, + otherwise sectors per index block. */ +/* 13*/ u8 reserved[3]; /* Reserved/align to 8-byte boundary. */ +/* 16*/ INDEX_HEADER index; /* Index header describing the + following index entries. */ +/* sizeof()= 32 bytes */ +} __attribute__((__packed__)) INDEX_ROOT; + +/** + * struct INDEX_BLOCK - Attribute: Index allocation (0xa0). + * + * NOTE: Always non-resident (doesn't make sense to be resident anyway!). + * + * This is an array of index blocks. Each index block starts with an + * INDEX_BLOCK structure containing an index header, followed by a sequence of + * index entries (INDEX_ENTRY structures), as described by the INDEX_HEADER. + */ +typedef struct { +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Magic is "INDX". */ + u16 usa_ofs; /* See NTFS_RECORD definition. */ + u16 usa_count; /* See NTFS_RECORD definition. */ + +/* 8*/ LSN lsn; /* $LogFile sequence number of the last + modification of this index block. */ +/* 16*/ VCN index_block_vcn; /* Virtual cluster number of the index block. */ +/* 24*/ INDEX_HEADER index; /* Describes the following index entries. */ +/* sizeof()= 40 (0x28) bytes */ +/* + * When creating the index block, we place the update sequence array at this + * offset, i.e. before we start with the index entries. This also makes sense, + * otherwise we could run into problems with the update sequence array + * containing in itself the last two bytes of a sector which would mean that + * multi sector transfer protection wouldn't work. As you can't protect data + * by overwriting it since you then can't get it back... + * When reading use the data from the ntfs record header. + */ +} __attribute__((__packed__)) INDEX_BLOCK; + +typedef INDEX_BLOCK INDEX_ALLOCATION; + +/** + * struct REPARSE_INDEX_KEY - + * + * The system file FILE_Extend/$Reparse contains an index named $R listing + * all reparse points on the volume. The index entry keys are as defined + * below. Note, that there is no index data associated with the index entries. + * + * The index entries are sorted by the index key file_id. The collation rule is + * COLLATION_NTOFS_ULONGS. FIXME: Verify whether the reparse_tag is not the + * primary key / is not a key at all. (AIA) + */ +typedef struct { + u32 reparse_tag; /* Reparse point type (inc. flags). */ + MFT_REF file_id; /* Mft record of the file containing the + reparse point attribute. */ +} __attribute__((__packed__)) REPARSE_INDEX_KEY; + +/** + * enum QUOTA_FLAGS - Quota flags (32-bit). + */ +typedef enum { + /* The user quota flags. Names explain meaning. */ + QUOTA_FLAG_DEFAULT_LIMITS = const_cpu_to_le32(0x00000001), + QUOTA_FLAG_LIMIT_REACHED = const_cpu_to_le32(0x00000002), + QUOTA_FLAG_ID_DELETED = const_cpu_to_le32(0x00000004), + + QUOTA_FLAG_USER_MASK = const_cpu_to_le32(0x00000007), + /* Bit mask for user quota flags. */ + + /* These flags are only present in the quota defaults index entry, + i.e. in the entry where owner_id = QUOTA_DEFAULTS_ID. */ + QUOTA_FLAG_TRACKING_ENABLED = const_cpu_to_le32(0x00000010), + QUOTA_FLAG_ENFORCEMENT_ENABLED = const_cpu_to_le32(0x00000020), + QUOTA_FLAG_TRACKING_REQUESTED = const_cpu_to_le32(0x00000040), + QUOTA_FLAG_LOG_THRESHOLD = const_cpu_to_le32(0x00000080), + QUOTA_FLAG_LOG_LIMIT = const_cpu_to_le32(0x00000100), + QUOTA_FLAG_OUT_OF_DATE = const_cpu_to_le32(0x00000200), + QUOTA_FLAG_CORRUPT = const_cpu_to_le32(0x00000400), + QUOTA_FLAG_PENDING_DELETES = const_cpu_to_le32(0x00000800), +} QUOTA_FLAGS; + +/** + * struct QUOTA_CONTROL_ENTRY - + * + * The system file FILE_Extend/$Quota contains two indexes $O and $Q. Quotas + * are on a per volume and per user basis. + * + * The $Q index contains one entry for each existing user_id on the volume. The + * index key is the user_id of the user/group owning this quota control entry, + * i.e. the key is the owner_id. The user_id of the owner of a file, i.e. the + * owner_id, is found in the standard information attribute. The collation rule + * for $Q is COLLATION_NTOFS_ULONG. + * + * The $O index contains one entry for each user/group who has been assigned + * a quota on that volume. The index key holds the SID of the user_id the + * entry belongs to, i.e. the owner_id. The collation rule for $O is + * COLLATION_NTOFS_SID. + * + * The $O index entry data is the user_id of the user corresponding to the SID. + * This user_id is used as an index into $Q to find the quota control entry + * associated with the SID. + * + * The $Q index entry data is the quota control entry and is defined below. + */ +typedef struct { + u32 version; /* Currently equals 2. */ + QUOTA_FLAGS flags; /* Flags describing this quota entry. */ + u64 bytes_used; /* How many bytes of the quota are in use. */ + s64 change_time; /* Last time this quota entry was changed. */ + s64 threshold; /* Soft quota (-1 if not limited). */ + s64 limit; /* Hard quota (-1 if not limited). */ + s64 exceeded_time; /* How long the soft quota has been exceeded. */ +/* The below field is NOT present for the quota defaults entry. */ + SID sid; /* The SID of the user/object associated with + this quota entry. If this field is missing + then the INDEX_ENTRY is padded to a multiple + of 8 with zeros which are not counted in + the data_length field. If the sid is present + then this structure is padded with zeros to + a multiple of 8 and the padding is counted in + the INDEX_ENTRY's data_length. */ +} __attribute__((__packed__)) QUOTA_CONTROL_ENTRY; + +/** + * struct QUOTA_O_INDEX_DATA - + */ +typedef struct { + u32 owner_id; + u32 unknown; /* Always 32. Seems to be padding and it's not + counted in the INDEX_ENTRY's data_length. + This field shouldn't be really here. */ +} __attribute__((__packed__)) QUOTA_O_INDEX_DATA; + +/** + * enum PREDEFINED_OWNER_IDS - Predefined owner_id values (32-bit). + */ +typedef enum { + QUOTA_INVALID_ID = const_cpu_to_le32(0x00000000), + QUOTA_DEFAULTS_ID = const_cpu_to_le32(0x00000001), + QUOTA_FIRST_USER_ID = const_cpu_to_le32(0x00000100), +} PREDEFINED_OWNER_IDS; + +/** + * enum INDEX_ENTRY_FLAGS - Index entry flags (16-bit). + */ +typedef enum { + INDEX_ENTRY_NODE = const_cpu_to_le16(1), /* This entry contains a + sub-node, i.e. a reference to an index + block in form of a virtual cluster + number (see below). */ + INDEX_ENTRY_END = const_cpu_to_le16(2), /* This signifies the last + entry in an index block. The index + entry does not represent a file but it + can point to a sub-node. */ + INDEX_ENTRY_SPACE_FILLER = 0xffff, /* Just to force 16-bit width. */ +} __attribute__((__packed__)) INDEX_ENTRY_FLAGS; + +/** + * struct INDEX_ENTRY_HEADER - This the index entry header (see below). + * + * ========================================================== + * !!!!! SEE DESCRIPTION OF THE FIELDS AT INDEX_ENTRY !!!!! + * ========================================================== + */ +typedef struct { +/* 0*/ union { + MFT_REF indexed_file; + struct { + u16 data_offset; + u16 data_length; + u32 reservedV; + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* 8*/ u16 length; +/* 10*/ u16 key_length; +/* 12*/ INDEX_ENTRY_FLAGS flags; +/* 14*/ u16 reserved; +/* sizeof() = 16 bytes */ +} __attribute__((__packed__)) INDEX_ENTRY_HEADER; + +/** + * struct INDEX_ENTRY - This is an index entry. + * + * A sequence of such entries follows each INDEX_HEADER structure. Together + * they make up a complete index. The index follows either an index root + * attribute or an index allocation attribute. + * + * NOTE: Before NTFS 3.0 only filename attributes were indexed. + */ +typedef struct { +/* 0 INDEX_ENTRY_HEADER; -- Unfolded here as gcc dislikes unnamed structs. */ + union { /* Only valid when INDEX_ENTRY_END is not set. */ + MFT_REF indexed_file; /* The mft reference of the file + described by this index + entry. Used for directory + indexes. */ + struct { /* Used for views/indexes to find the entry's data. */ + u16 data_offset; /* Data byte offset from this + INDEX_ENTRY. Follows the + index key. */ + u16 data_length; /* Data length in bytes. */ + u32 reservedV; /* Reserved (zero). */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +/* 8*/ u16 length; /* Byte size of this index entry, multiple of + 8-bytes. Size includes INDEX_ENTRY_HEADER + and the optional subnode VCN. See below. */ +/* 10*/ u16 key_length; /* Byte size of the key value, which is in the + index entry. It follows field reserved. Not + multiple of 8-bytes. */ +/* 12*/ INDEX_ENTRY_FLAGS ie_flags; /* Bit field of INDEX_ENTRY_* flags. */ +/* 14*/ u16 reserved; /* Reserved/align to 8-byte boundary. */ +/* End of INDEX_ENTRY_HEADER */ +/* 16*/ union { /* The key of the indexed attribute. NOTE: Only present + if INDEX_ENTRY_END bit in flags is not set. NOTE: On + NTFS versions before 3.0 the only valid key is the + FILE_NAME_ATTR. On NTFS 3.0+ the following + additional index keys are defined: */ + FILE_NAME_ATTR file_name;/* $I30 index in directories. */ + SII_INDEX_KEY sii; /* $SII index in $Secure. */ + SDH_INDEX_KEY sdh; /* $SDH index in $Secure. */ + GUID object_id; /* $O index in FILE_Extend/$ObjId: The + object_id of the mft record found in + the data part of the index. */ + REPARSE_INDEX_KEY reparse; /* $R index in + FILE_Extend/$Reparse. */ + SID sid; /* $O index in FILE_Extend/$Quota: + SID of the owner of the user_id. */ + u32 owner_id; /* $Q index in FILE_Extend/$Quota: + user_id of the owner of the quota + control entry in the data part of + the index. */ + } __attribute__((__packed__)) key; + /* The (optional) index data is inserted here when creating. + VCN vcn; If INDEX_ENTRY_NODE bit in ie_flags is set, the last + eight bytes of this index entry contain the virtual + cluster number of the index block that holds the + entries immediately preceding the current entry. + + If the key_length is zero, then the vcn immediately + follows the INDEX_ENTRY_HEADER. + + The address of the vcn of "ie" INDEX_ENTRY is given by + (char*)ie + le16_to_cpu(ie->length) - sizeof(VCN) + */ +} __attribute__((__packed__)) INDEX_ENTRY; + +/** + * struct BITMAP_ATTR - Attribute: Bitmap (0xb0). + * + * Contains an array of bits (aka a bitfield). + * + * When used in conjunction with the index allocation attribute, each bit + * corresponds to one index block within the index allocation attribute. Thus + * the number of bits in the bitmap * index block size / cluster size is the + * number of clusters in the index allocation attribute. + */ +typedef struct { + u8 bitmap[0]; /* Array of bits. */ +} __attribute__((__packed__)) BITMAP_ATTR; + +/** + * enum PREDEFINED_REPARSE_TAGS - + * + * The reparse point tag defines the type of the reparse point. It also + * includes several flags, which further describe the reparse point. + * + * The reparse point tag is an unsigned 32-bit value divided in three parts: + * + * 1. The least significant 16 bits (i.e. bits 0 to 15) specify the type of + * the reparse point. + * 2. The 13 bits after this (i.e. bits 16 to 28) are reserved for future use. + * 3. The most significant three bits are flags describing the reparse point. + * They are defined as follows: + * bit 29: Name surrogate bit. If set, the filename is an alias for + * another object in the system. + * bit 30: High-latency bit. If set, accessing the first byte of data will + * be slow. (E.g. the data is stored on a tape drive.) + * bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User + * defined tags have to use zero here. + */ +typedef enum { + IO_REPARSE_TAG_IS_ALIAS = const_cpu_to_le32(0x20000000), + IO_REPARSE_TAG_IS_HIGH_LATENCY = const_cpu_to_le32(0x40000000), + IO_REPARSE_TAG_IS_MICROSOFT = const_cpu_to_le32(0x80000000), + + IO_REPARSE_TAG_RESERVED_ZERO = const_cpu_to_le32(0x00000000), + IO_REPARSE_TAG_RESERVED_ONE = const_cpu_to_le32(0x00000001), + IO_REPARSE_TAG_RESERVED_RANGE = const_cpu_to_le32(0x00000001), + + IO_REPARSE_TAG_CSV = const_cpu_to_le32(0x80000009), + IO_REPARSE_TAG_DEDUP = const_cpu_to_le32(0x80000013), + IO_REPARSE_TAG_DFS = const_cpu_to_le32(0x8000000A), + IO_REPARSE_TAG_DFSR = const_cpu_to_le32(0x80000012), + IO_REPARSE_TAG_HSM = const_cpu_to_le32(0xC0000004), + IO_REPARSE_TAG_HSM2 = const_cpu_to_le32(0x80000006), + IO_REPARSE_TAG_MOUNT_POINT = const_cpu_to_le32(0xA0000003), + IO_REPARSE_TAG_NFS = const_cpu_to_le32(0x80000014), + IO_REPARSE_TAG_SIS = const_cpu_to_le32(0x80000007), + IO_REPARSE_TAG_SYMLINK = const_cpu_to_le32(0xA000000C), + IO_REPARSE_TAG_WIM = const_cpu_to_le32(0x80000008), + + IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xf000ffff), +} PREDEFINED_REPARSE_TAGS; + +/** + * struct REPARSE_POINT - Attribute: Reparse point (0xc0). + * + * NOTE: Can be resident or non-resident. + */ +typedef struct { + u32 reparse_tag; /* Reparse point type (inc. flags). */ + u16 reparse_data_length; /* Byte size of reparse data. */ + u16 reserved; /* Align to 8-byte boundary. */ + u8 reparse_data[0]; /* Meaning depends on reparse_tag. */ +} __attribute__((__packed__)) REPARSE_POINT; + +/** + * struct EA_INFORMATION - Attribute: Extended attribute information (0xd0). + * + * NOTE: Always resident. + */ +typedef struct { + u16 ea_length; /* Byte size of the packed extended + attributes. */ + u16 need_ea_count; /* The number of extended attributes which have + the NEED_EA bit set. */ + u32 ea_query_length; /* Byte size of the buffer required to query + the extended attributes when calling + ZwQueryEaFile() in Windows NT/2k. I.e. the + byte size of the unpacked extended + attributes. */ +} __attribute__((__packed__)) EA_INFORMATION; + +/** + * enum EA_FLAGS - Extended attribute flags (8-bit). + */ +typedef enum { + NEED_EA = 0x80, /* Indicate that the file to which the EA + belongs cannot be interpreted without + understanding the associated extended + attributes. */ +} __attribute__((__packed__)) EA_FLAGS; + +/** + * struct EA_ATTR - Attribute: Extended attribute (EA) (0xe0). + * + * Like the attribute list and the index buffer list, the EA attribute value is + * a sequence of EA_ATTR variable length records. + * + * FIXME: It appears weird that the EA name is not Unicode. Is it true? + * FIXME: It seems that name is always uppercased. Is it true? + */ +typedef struct { + u32 next_entry_offset; /* Offset to the next EA_ATTR. */ + EA_FLAGS flags; /* Flags describing the EA. */ + u8 name_length; /* Length of the name of the extended + attribute in bytes. */ + u16 value_length; /* Byte size of the EA's value. */ + u8 name[0]; /* Name of the EA. */ + u8 value[0]; /* The value of the EA. Immediately + follows the name. */ +} __attribute__((__packed__)) EA_ATTR; + +/** + * struct PROPERTY_SET - Attribute: Property set (0xf0). + * + * Intended to support Native Structure Storage (NSS) - a feature removed from + * NTFS 3.0 during beta testing. + */ +typedef struct { + /* Irrelevant as feature unused. */ +} __attribute__((__packed__)) PROPERTY_SET; + +/** + * struct LOGGED_UTILITY_STREAM - Attribute: Logged utility stream (0x100). + * + * NOTE: Can be resident or non-resident. + * + * Operations on this attribute are logged to the journal ($LogFile) like + * normal metadata changes. + * + * Used by the Encrypting File System (EFS). All encrypted files have this + * attribute with the name $EFS. See below for the relevant structures. + */ +typedef struct { + /* Can be anything the creator chooses. */ +} __attribute__((__packed__)) LOGGED_UTILITY_STREAM; + +/* + * $EFS Data Structure: + * + * The following information is about the data structures that are contained + * inside a logged utility stream (0x100) with a name of "$EFS". + * + * The stream starts with an instance of EFS_ATTR_HEADER. + * + * Next, at offsets offset_to_ddf_array and offset_to_drf_array (unless any of + * them is 0) there is a EFS_DF_ARRAY_HEADER immediately followed by a sequence + * of multiple data decryption/recovery fields. + * + * Each data decryption/recovery field starts with a EFS_DF_HEADER and the next + * one (if it exists) can be found by adding EFS_DF_HEADER->df_length bytes to + * the offset of the beginning of the current EFS_DF_HEADER. + * + * The data decryption/recovery field contains an EFS_DF_CERTIFICATE_HEADER, a + * SID, an optional GUID, an optional container name, a non-optional user name, + * and the encrypted FEK. + * + * Note all the below are best guesses so may have mistakes/inaccuracies. + * Corrections/clarifications/additions are always welcome! + * + * Ntfs.sys takes an EFS value length of <= 0x54 or > 0x40000 to BSOD, i.e. it + * is invalid. + */ + +/** + * struct EFS_ATTR_HEADER - "$EFS" header. + * + * The header of the Logged utility stream (0x100) attribute named "$EFS". + */ +typedef struct { +/* 0*/ u32 length; /* Length of EFS attribute in bytes. */ + u32 state; /* Always 0? */ + u32 version; /* Efs version. Always 2? */ + u32 crypto_api_version; /* Always 0? */ +/* 16*/ u8 unknown4[16]; /* MD5 hash of decrypted FEK? This field is + created with a call to UuidCreate() so is + unlikely to be an MD5 hash and is more + likely to be GUID of this encrytped file + or something like that. */ +/* 32*/ u8 unknown5[16]; /* MD5 hash of DDFs? */ +/* 48*/ u8 unknown6[16]; /* MD5 hash of DRFs? */ +/* 64*/ u32 offset_to_ddf_array;/* Offset in bytes to the array of data + decryption fields (DDF), see below. Zero if + no DDFs are present. */ + u32 offset_to_drf_array;/* Offset in bytes to the array of data + recovery fields (DRF), see below. Zero if + no DRFs are present. */ + u32 reserved; /* Reserved. */ +} __attribute__((__packed__)) EFS_ATTR_HEADER; + +/** + * struct EFS_DF_ARRAY_HEADER - + */ +typedef struct { + u32 df_count; /* Number of data decryption/recovery fields in + the array. */ +} __attribute__((__packed__)) EFS_DF_ARRAY_HEADER; + +/** + * struct EFS_DF_HEADER - + */ +typedef struct { +/* 0*/ u32 df_length; /* Length of this data decryption/recovery + field in bytes. */ + u32 cred_header_offset; /* Offset in bytes to the credential header. */ + u32 fek_size; /* Size in bytes of the encrypted file + encryption key (FEK). */ + u32 fek_offset; /* Offset in bytes to the FEK from the start of + the data decryption/recovery field. */ +/* 16*/ u32 unknown1; /* always 0? Might be just padding. */ +} __attribute__((__packed__)) EFS_DF_HEADER; + +/** + * struct EFS_DF_CREDENTIAL_HEADER - + */ +typedef struct { +/* 0*/ u32 cred_length; /* Length of this credential in bytes. */ + u32 sid_offset; /* Offset in bytes to the user's sid from start + of this structure. Zero if no sid is + present. */ +/* 8*/ u32 type; /* Type of this credential: + 1 = CryptoAPI container. + 2 = Unexpected type. + 3 = Certificate thumbprint. + other = Unknown type. */ + union { + /* CryptoAPI container. */ + struct { +/* 12*/ u32 container_name_offset; /* Offset in bytes to + the name of the container from start of this + structure (may not be zero). */ +/* 16*/ u32 provider_name_offset; /* Offset in bytes to + the name of the provider from start of this + structure (may not be zero). */ + u32 public_key_blob_offset; /* Offset in bytes to + the public key blob from start of this + structure. */ +/* 24*/ u32 public_key_blob_size; /* Size in bytes of + public key blob. */ + } __attribute__((__packed__)); + /* Certificate thumbprint. */ + struct { +/* 12*/ u32 cert_thumbprint_header_size; /* Size in + bytes of the header of the certificate + thumbprint. */ +/* 16*/ u32 cert_thumbprint_header_offset; /* Offset in + bytes to the header of the certificate + thumbprint from start of this structure. */ + u32 unknown1; /* Always 0? Might be padding... */ + u32 unknown2; /* Always 0? Might be padding... */ + } __attribute__((__packed__)); + } __attribute__((__packed__)); +} __attribute__((__packed__)) EFS_DF_CREDENTIAL_HEADER; + +typedef EFS_DF_CREDENTIAL_HEADER EFS_DF_CRED_HEADER; + +/** + * struct EFS_DF_CERTIFICATE_THUMBPRINT_HEADER - + */ +typedef struct { +/* 0*/ u32 thumbprint_offset; /* Offset in bytes to the thumbprint. */ + u32 thumbprint_size; /* Size of thumbprint in bytes. */ +/* 8*/ u32 container_name_offset; /* Offset in bytes to the name of the + container from start of this + structure or 0 if no name present. */ + u32 provider_name_offset; /* Offset in bytes to the name of the + cryptographic provider from start of + this structure or 0 if no name + present. */ +/* 16*/ u32 user_name_offset; /* Offset in bytes to the user name + from start of this structure or 0 if + no user name present. (This is also + known as lpDisplayInformation.) */ +} __attribute__((__packed__)) EFS_DF_CERTIFICATE_THUMBPRINT_HEADER; + +typedef EFS_DF_CERTIFICATE_THUMBPRINT_HEADER EFS_DF_CERT_THUMBPRINT_HEADER; + +typedef enum { + INTX_SYMBOLIC_LINK = + const_cpu_to_le64(0x014B4E4C78746E49ULL), /* "IntxLNK\1" */ + INTX_CHARACTER_DEVICE = + const_cpu_to_le64(0x0052484378746E49ULL), /* "IntxCHR\0" */ + INTX_BLOCK_DEVICE = + const_cpu_to_le64(0x004B4C4278746E49ULL), /* "IntxBLK\0" */ +} INTX_FILE_TYPES; + +typedef struct { + INTX_FILE_TYPES magic; /* Intx file magic. */ + union { + /* For character and block devices. */ + struct { + u64 major; /* Major device number. */ + u64 minor; /* Minor device number. */ + void *device_end[0]; /* Marker for offsetof(). */ + } __attribute__((__packed__)); + /* For symbolic links. */ + ntfschar target[0]; + } __attribute__((__packed__)); +} __attribute__((__packed__)) INTX_FILE; + +#endif /* defined _NTFS_LAYOUT_H */ diff --git a/include/ntfs-3g/lcnalloc.h b/include/ntfs-3g/lcnalloc.h new file mode 100755 index 0000000000000000000000000000000000000000..cbf4c5cd7a06e22022e31cd703e3459b1e787b60 --- /dev/null +++ b/include/ntfs-3g/lcnalloc.h @@ -0,0 +1,51 @@ +/* + * lcnalloc.h - Exports for cluster (de)allocation. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2004 Yura Pakhuchiy + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_LCNALLOC_H +#define _NTFS_LCNALLOC_H + +#include "types.h" +#include "runlist.h" +#include "volume.h" + +/** + * enum NTFS_CLUSTER_ALLOCATION_ZONES - + */ +typedef enum { + FIRST_ZONE = 0, /* For sanity checking. */ + MFT_ZONE = 0, /* Allocate from $MFT zone. */ + DATA_ZONE = 1, /* Allocate from $DATA zone. */ + LAST_ZONE = 1, /* For sanity checking. */ +} NTFS_CLUSTER_ALLOCATION_ZONES; + +extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, + LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone); + +extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl); +extern int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count); + +extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, + s64 count); + +#endif /* defined _NTFS_LCNALLOC_H */ + diff --git a/include/ntfs-3g/logfile.h b/include/ntfs-3g/logfile.h new file mode 100755 index 0000000000000000000000000000000000000000..798d562d17ce720873756a765dbfc514d6b80b8b --- /dev/null +++ b/include/ntfs-3g/logfile.h @@ -0,0 +1,394 @@ +/* + * logfile.h - Exports for $LogFile handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_LOGFILE_H +#define _NTFS_LOGFILE_H + +#include "types.h" +#include "endians.h" +#include "layout.h" + +/* + * Journal ($LogFile) organization: + * + * Two restart areas present in the first two pages (restart pages, one restart + * area in each page). When the volume is dismounted they should be identical, + * except for the update sequence array which usually has a different update + * sequence number. + * + * These are followed by log records organized in pages headed by a log record + * header going up to log file size. Not all pages contain log records when a + * volume is first formatted, but as the volume ages, all records will be used. + * When the log file fills up, the records at the beginning are purged (by + * modifying the oldest_lsn to a higher value presumably) and writing begins + * at the beginning of the file. Effectively, the log file is viewed as a + * circular entity. + * + * NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept + * versions <= 1.x, including 0.-1. (Yes, that is a minus one in there!) We + * probably only want to support 1.1 as this seems to be the current version + * and we don't know how that differs from the older versions. The only + * exception is if the journal is clean as marked by the two restart pages + * then it doesn't matter whether we are on an earlier version. We can just + * reinitialize the logfile and start again with version 1.1. + */ + +/* Some $LogFile related constants. */ +#define MaxLogFileSize 0x100000000ULL +#define DefaultLogPageSize 4096 +#define MinLogRecordPages 48 + +/** + * struct RESTART_PAGE_HEADER - Log file restart page header. + * + * Begins the restart area. + */ +typedef struct { +/*Ofs*/ +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ +/* 0*/ NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */ +/* 4*/ le16 usa_ofs; /* See NTFS_RECORD definition in layout.h. + When creating, set this to be immediately + after this header structure (without any + alignment). */ +/* 6*/ le16 usa_count; /* See NTFS_RECORD definition in layout.h. */ + +/* 8*/ leLSN chkdsk_lsn; /* The last log file sequence number found by + chkdsk. Only used when the magic is changed + to "CHKD". Otherwise this is zero. */ +/* 16*/ le32 system_page_size; /* Byte size of system pages when the log file + was created, has to be >= 512 and a power of + 2. Use this to calculate the required size + of the usa (usa_count) and add it to usa_ofs. + Then verify that the result is less than the + value of the restart_area_offset. */ +/* 20*/ le32 log_page_size; /* Byte size of log file pages, has to be >= + 512 and a power of 2. The default is 4096 + and is used when the system page size is + between 4096 and 8192. Otherwise this is + set to the system page size instead. */ +/* 24*/ le16 restart_area_offset;/* Byte offset from the start of this header to + the RESTART_AREA. Value has to be aligned + to 8-byte boundary. When creating, set this + to be after the usa. */ +/* 26*/ sle16 minor_ver; /* Log file minor version. Only check if major + version is 1. */ +/* 28*/ sle16 major_ver; /* Log file major version. We only support + version 1.1. */ +/* sizeof() = 30 (0x1e) bytes */ +} __attribute__((__packed__)) RESTART_PAGE_HEADER; + +/* + * Constant for the log client indices meaning that there are no client records + * in this particular client array. Also inside the client records themselves, + * this means that there are no client records preceding or following this one. + */ +#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff) +#define LOGFILE_NO_CLIENT_CPU 0xffff + +/* + * These are the so far known RESTART_AREA_* flags (16-bit) which contain + * information about the log file in which they are present. + */ +enum { + RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002), + RESTART_SPACE_FILLER = 0xffff, /* gcc: Force enum bit width to 16. */ +} __attribute__((__packed__)); + +typedef le16 RESTART_AREA_FLAGS; + +/** + * struct RESTART_AREA - Log file restart area record. + * + * The offset of this record is found by adding the offset of the + * RESTART_PAGE_HEADER to the restart_area_offset value found in it. + * See notes at restart_area_offset above. + */ +typedef struct { +/*Ofs*/ +/* 0*/ leLSN current_lsn; /* The current, i.e. last LSN inside the log + when the restart area was last written. + This happens often but what is the interval? + Is it just fixed time or is it every time a + check point is written or something else? + On create set to 0. */ +/* 8*/ le16 log_clients; /* Number of log client records in the array of + log client records which follows this + restart area. Must be 1. */ +/* 10*/ le16 client_free_list; /* The index of the first free log client record + in the array of log client records. + LOGFILE_NO_CLIENT means that there are no + free log client records in the array. + If != LOGFILE_NO_CLIENT, check that + log_clients > client_free_list. On Win2k + and presumably earlier, on a clean volume + this is != LOGFILE_NO_CLIENT, and it should + be 0, i.e. the first (and only) client + record is free and thus the logfile is + closed and hence clean. A dirty volume + would have left the logfile open and hence + this would be LOGFILE_NO_CLIENT. On WinXP + and presumably later, the logfile is always + open, even on clean shutdown so this should + always be LOGFILE_NO_CLIENT. */ +/* 12*/ le16 client_in_use_list;/* The index of the first in-use log client + record in the array of log client records. + LOGFILE_NO_CLIENT means that there are no + in-use log client records in the array. If + != LOGFILE_NO_CLIENT check that log_clients + > client_in_use_list. On Win2k and + presumably earlier, on a clean volume this + is LOGFILE_NO_CLIENT, i.e. there are no + client records in use and thus the logfile + is closed and hence clean. A dirty volume + would have left the logfile open and hence + this would be != LOGFILE_NO_CLIENT, and it + should be 0, i.e. the first (and only) + client record is in use. On WinXP and + presumably later, the logfile is always + open, even on clean shutdown so this should + always be 0. */ +/* 14*/ RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k + and presumably earlier this is always 0. On + WinXP and presumably later, if the logfile + was shutdown cleanly, the second bit, + RESTART_VOLUME_IS_CLEAN, is set. This bit + is cleared when the volume is mounted by + WinXP and set when the volume is dismounted, + thus if the logfile is dirty, this bit is + clear. Thus we don't need to check the + Windows version to determine if the logfile + is clean. Instead if the logfile is closed, + we know it must be clean. If it is open and + this bit is set, we also know it must be + clean. If on the other hand the logfile is + open and this bit is clear, we can be almost + certain that the logfile is dirty. */ +/* 16*/ le32 seq_number_bits; /* How many bits to use for the sequence + number. This is calculated as 67 - the + number of bits required to store the logfile + size in bytes and this can be used in with + the specified file_size as a consistency + check. */ +/* 20*/ le16 restart_area_length;/* Length of the restart area including the + client array. Following checks required if + version matches. Otherwise, skip them. + restart_area_offset + restart_area_length + has to be <= system_page_size. Also, + restart_area_length has to be >= + client_array_offset + (log_clients * + sizeof(log client record)). */ +/* 22*/ le16 client_array_offset;/* Offset from the start of this record to + the first log client record if versions are + matched. When creating, set this to be + after this restart area structure, aligned + to 8-bytes boundary. If the versions do not + match, this is ignored and the offset is + assumed to be (sizeof(RESTART_AREA) + 7) & + ~7, i.e. rounded up to first 8-byte + boundary. Either way, client_array_offset + has to be aligned to an 8-byte boundary. + Also, restart_area_offset + + client_array_offset has to be <= 510. + Finally, client_array_offset + (log_clients + * sizeof(log client record)) has to be <= + system_page_size. On Win2k and presumably + earlier, this is 0x30, i.e. immediately + following this record. On WinXP and + presumably later, this is 0x40, i.e. there + are 16 extra bytes between this record and + the client array. This probably means that + the RESTART_AREA record is actually bigger + in WinXP and later. */ +/* 24*/ sle64 file_size; /* Usable byte size of the log file. If the + restart_area_offset + the offset of the + file_size are > 510 then corruption has + occurred. This is the very first check when + starting with the restart_area as if it + fails it means that some of the above values + will be corrupted by the multi sector + transfer protection. The file_size has to + be rounded down to be a multiple of the + log_page_size in the RESTART_PAGE_HEADER and + then it has to be at least big enough to + store the two restart pages and 48 (0x30) + log record pages. */ +/* 32*/ le32 last_lsn_data_length;/* Length of data of last LSN, not including + the log record header. On create set to + 0. */ +/* 36*/ le16 log_record_header_length;/* Byte size of the log record header. + If the version matches then check that the + value of log_record_header_length is a + multiple of 8, i.e. + (log_record_header_length + 7) & ~7 == + log_record_header_length. When creating set + it to sizeof(LOG_RECORD_HEADER), aligned to + 8 bytes. */ +/* 38*/ le16 log_page_data_offset;/* Offset to the start of data in a log record + page. Must be a multiple of 8. On create + set it to immediately after the update + sequence array of the log record page. */ +/* 40*/ le32 restart_log_open_count;/* A counter that gets incremented every + time the logfile is restarted which happens + at mount time when the logfile is opened. + When creating set to a random value. Win2k + sets it to the low 32 bits of the current + system time in NTFS format (see time.h). */ +/* 44*/ le32 reserved; /* Reserved/alignment to 8-byte boundary. */ +/* sizeof() = 48 (0x30) bytes */ +} __attribute__((__packed__)) RESTART_AREA; + +/** + * struct LOG_CLIENT_RECORD - Log client record. + * + * The offset of this record is found by adding the offset of the + * RESTART_AREA to the client_array_offset value found in it. + */ +typedef struct { +/*Ofs*/ +/* 0*/ leLSN oldest_lsn; /* Oldest LSN needed by this client. On create + set to 0. */ +/* 8*/ leLSN client_restart_lsn;/* LSN at which this client needs to restart + the volume, i.e. the current position within + the log file. At present, if clean this + should = current_lsn in restart area but it + probably also = current_lsn when dirty most + of the time. At create set to 0. */ +/* 16*/ le16 prev_client; /* The offset to the previous log client record + in the array of log client records. + LOGFILE_NO_CLIENT means there is no previous + client record, i.e. this is the first one. + This is always LOGFILE_NO_CLIENT. */ +/* 18*/ le16 next_client; /* The offset to the next log client record in + the array of log client records. + LOGFILE_NO_CLIENT means there are no next + client records, i.e. this is the last one. + This is always LOGFILE_NO_CLIENT. */ +/* 20*/ le16 seq_number; /* On Win2k and presumably earlier, this is set + to zero every time the logfile is restarted + and it is incremented when the logfile is + closed at dismount time. Thus it is 0 when + dirty and 1 when clean. On WinXP and + presumably later, this is always 0. */ +/* 22*/ u8 reserved[6]; /* Reserved/alignment. */ +/* 28*/ le32 client_name_length;/* Length of client name in bytes. Should + always be 8. */ +/* 32*/ ntfschar client_name[64];/* Name of the client in Unicode. Should + always be "NTFS" with the remaining bytes + set to 0. */ +/* sizeof() = 160 (0xa0) bytes */ +} __attribute__((__packed__)) LOG_CLIENT_RECORD; + +/** + * struct RECORD_PAGE_HEADER - Log page record page header. + * + * Each log page begins with this header and is followed by several LOG_RECORD + * structures, starting at offset 0x40 (the size of this structure and the + * following update sequence array and then aligned to 8 byte boundary, but is + * this specified anywhere?). + */ +typedef struct { +/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */ + NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */ + u16 usa_ofs; /* See NTFS_RECORD definition in layout.h. + When creating, set this to be immediately + after this header structure (without any + alignment). */ + u16 usa_count; /* See NTFS_RECORD definition in layout.h. */ + + union { + LSN last_lsn; + s64 file_offset; + } __attribute__((__packed__)) copy; + u32 flags; + u16 page_count; + u16 page_position; + union { + struct { + u16 next_record_offset; + u8 reserved[6]; + LSN last_end_lsn; + } __attribute__((__packed__)) packed; + } __attribute__((__packed__)) header; +} __attribute__((__packed__)) RECORD_PAGE_HEADER; + +/** + * enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records. + * + * (Or is it log record pages?) + */ +typedef enum { + LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */ + LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff, + /* This has nothing to do with the log record. It is only so + gcc knows to make the flags 16-bit. */ +} __attribute__((__packed__)) LOG_RECORD_FLAGS; + +/** + * struct LOG_CLIENT_ID - The log client id structure identifying a log client. + */ +typedef struct { + u16 seq_number; + u16 client_index; +} __attribute__((__packed__)) LOG_CLIENT_ID; + +/** + * struct LOG_RECORD - Log record header. + * + * Each log record seems to have a constant size of 0x70 bytes. + */ +typedef struct { + LSN this_lsn; + LSN client_previous_lsn; + LSN client_undo_next_lsn; + u32 client_data_length; + LOG_CLIENT_ID client_id; + u32 record_type; + u32 transaction_id; + u16 flags; + u16 reserved_or_alignment[3]; +/* Now are at ofs 0x30 into struct. */ + u16 redo_operation; + u16 undo_operation; + u16 redo_offset; + u16 redo_length; + u16 undo_offset; + u16 undo_length; + u16 target_attribute; + u16 lcns_to_follow; /* Number of lcn_list entries + following this entry. */ +/* Now at ofs 0x40. */ + u16 record_offset; + u16 attribute_offset; + u32 alignment_or_reserved; + VCN target_vcn; +/* Now at ofs 0x50. */ + struct { /* Only present if lcns_to_follow + is not 0. */ + LCN lcn; + } __attribute__((__packed__)) lcn_list[0]; +} __attribute__((__packed__)) LOG_RECORD; + +extern BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp); +extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp); +extern int ntfs_empty_logfile(ntfs_attr *na); + +#endif /* defined _NTFS_LOGFILE_H */ diff --git a/include/ntfs-3g/logging.h b/include/ntfs-3g/logging.h new file mode 100755 index 0000000000000000000000000000000000000000..82f39fe1661f78469688e7dfd86480ddca1fffaf --- /dev/null +++ b/include/ntfs-3g/logging.h @@ -0,0 +1,121 @@ +/* + * logging.h - Centralised logging. Originated from the Linux-NTFS project. + * + * Copyright (c) 2005 Richard Russon + * Copyright (c) 2007-2008 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif + +#include "types.h" + +/* Function prototype for the logging handlers */ +typedef int (ntfs_log_handler)(const char *function, const char *file, int line, + u32 level, void *data, const char *format, va_list args); + +/* Set the logging handler from one of the functions, below. */ +void ntfs_log_set_handler(ntfs_log_handler *handler + __attribute__((format(printf, 6, 0)))); + +/* Logging handlers */ +ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_fprintf __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_null __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_stdout __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_outerr __attribute__((format(printf, 6, 0))); +ntfs_log_handler ntfs_log_handler_stderr __attribute__((format(printf, 6, 0))); + +/* Enable/disable certain log levels */ +u32 ntfs_log_set_levels(u32 levels); +u32 ntfs_log_clear_levels(u32 levels); +u32 ntfs_log_get_levels(void); + +/* Enable/disable certain log flags */ +u32 ntfs_log_set_flags(u32 flags); +u32 ntfs_log_clear_flags(u32 flags); +u32 ntfs_log_get_flags(void); + +/* Turn command-line options into logging flags */ +BOOL ntfs_log_parse_option(const char *option); + +int ntfs_log_redirect(const char *function, const char *file, int line, + u32 level, void *data, const char *format, ...) + __attribute__((format(printf, 6, 7))); + +/* Logging levels - Determine what gets logged */ +#define NTFS_LOG_LEVEL_DEBUG (1 << 0) /* x = 42 */ +#define NTFS_LOG_LEVEL_TRACE (1 << 1) /* Entering function x() */ +#define NTFS_LOG_LEVEL_QUIET (1 << 2) /* Quietable output */ +#define NTFS_LOG_LEVEL_INFO (1 << 3) /* Volume needs defragmenting */ +#define NTFS_LOG_LEVEL_VERBOSE (1 << 4) /* Forced to continue */ +#define NTFS_LOG_LEVEL_PROGRESS (1 << 5) /* 54% complete */ +#define NTFS_LOG_LEVEL_WARNING (1 << 6) /* You should backup before starting */ +#define NTFS_LOG_LEVEL_ERROR (1 << 7) /* Operation failed, no damage done */ +#define NTFS_LOG_LEVEL_PERROR (1 << 8) /* Message : standard error description */ +#define NTFS_LOG_LEVEL_CRITICAL (1 << 9) /* Operation failed,damage may have occurred */ +#define NTFS_LOG_LEVEL_ENTER (1 << 10) /* Enter a function */ +#define NTFS_LOG_LEVEL_LEAVE (1 << 11) /* Leave a function */ + +/* Logging style flags - Manage the style of the output */ +#define NTFS_LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */ +#define NTFS_LOG_FLAG_FILENAME (1 << 1) /* Show the file origin of the message */ +#define NTFS_LOG_FLAG_LINE (1 << 2) /* Show the line number of the message */ +#define NTFS_LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */ +#define NTFS_LOG_FLAG_ONLYNAME (1 << 4) /* Only display the filename, not the pathname */ + +/* Macros to simplify logging. One for each level defined above. + * Note, ntfs_log_debug/trace have effect only if DEBUG is defined. + */ +#define ntfs_log_critical(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,FORMAT,##ARGS) +#define ntfs_log_error(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS) +#define ntfs_log_info(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,FORMAT,##ARGS) +#define ntfs_log_perror(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,FORMAT,##ARGS) +#define ntfs_log_progress(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,FORMAT,##ARGS) +#define ntfs_log_quiet(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,FORMAT,##ARGS) +#define ntfs_log_verbose(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,FORMAT,##ARGS) +#define ntfs_log_warning(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,FORMAT,##ARGS) + +/* By default debug and trace messages are compiled into the program, + * but not displayed. + */ +#ifdef DEBUG +#define ntfs_log_debug(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS) +#define ntfs_log_trace(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS) +#define ntfs_log_enter(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ENTER,NULL,FORMAT,##ARGS) +#define ntfs_log_leave(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_LEAVE,NULL,FORMAT,##ARGS) +#else +#define ntfs_log_debug(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_trace(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_enter(FORMAT, ARGS...)do {} while (0) +#define ntfs_log_leave(FORMAT, ARGS...)do {} while (0) +#endif /* DEBUG */ + +void ntfs_log_early_error(const char *format, ...) + __attribute__((format(printf, 1, 2))); + +#endif /* _LOGGING_H_ */ + diff --git a/include/ntfs-3g/mft.h b/include/ntfs-3g/mft.h new file mode 100755 index 0000000000000000000000000000000000000000..b135efce1250c4b553875e3c02b60e1330b75b05 --- /dev/null +++ b/include/ntfs-3g/mft.h @@ -0,0 +1,134 @@ +/* + * mft.h - Exports for MFT record handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2006-2008 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_MFT_H +#define _NTFS_MFT_H + +#include "volume.h" +#include "inode.h" +#include "layout.h" +#include "logging.h" + +extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b); + +/** + * ntfs_mft_record_read - read a record from the mft + * @vol: volume to read from + * @mref: mft record number to read + * @b: output data buffer + * + * Read the mft record specified by @mref from volume @vol into buffer @b. + * Return 0 on success or -1 on error, with errno set to the error code. + * + * The read mft record is mst deprotected and is hence ready to use. The caller + * should check the record with is_baad_record() in case mst deprotection + * failed. + * + * NOTE: @b has to be at least of size vol->mft_record_size. + */ +static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol, + const MFT_REF mref, MFT_RECORD *b) +{ + int ret; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + ret = ntfs_mft_records_read(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; +} + +extern int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m); + +extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD **mrec, ATTR_RECORD **attr); + +extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b); + +/** + * ntfs_mft_record_write - write an mft record to disk + * @vol: volume to write to + * @mref: mft record number to write + * @b: data buffer containing the mft record to write + * + * Write the mft record specified by @mref from buffer @b to volume @vol. + * Return 0 on success or -1 on error, with errno set to the error code. + * + * Before the mft record is written, it is mst protected. After the write, it + * is deprotected again, thus resulting in an increase in the update sequence + * number inside the buffer @b. + * + * NOTE: @b has to be at least of size vol->mft_record_size. + */ +static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol, + const MFT_REF mref, MFT_RECORD *b) +{ + int ret; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + ret = ntfs_mft_records_write(vol, mref, 1, b); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_mft_record_get_data_size - return number of bytes used in mft record @b + * @m: mft record to get the data size of + * + * Takes the mft record @m and returns the number of bytes used in the record + * or 0 on error (i.e. @m is not a valid mft record). Zero is not a valid size + * for an mft record as it at least has to have the MFT_RECORD itself and a + * zero length attribute of type AT_END, thus making the minimum size 56 bytes. + * + * Aside: The size is independent of NTFS versions 1.x/3.x because the 8-byte + * alignment of the first attribute mask the difference in MFT_RECORD size + * between NTFS 1.x and 3.x. Also, you would expect every mft record to + * contain an update sequence array as well but that could in theory be + * non-existent (don't know if Windows' NTFS driver/chkdsk wouldn't view this + * as corruption in itself though). + */ +static __inline__ u32 ntfs_mft_record_get_data_size(const MFT_RECORD *m) +{ + if (!m || !ntfs_is_mft_record(m->magic)) + return 0; + /* Get the number of used bytes and return it. */ + return le32_to_cpu(m->bytes_in_use); +} + +extern int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *mrec); + +extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref); + +extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni); + +extern ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol, BOOL mft_data); + +extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni); + +extern int ntfs_mft_usn_dec(MFT_RECORD *mrec); + +#endif /* defined _NTFS_MFT_H */ + diff --git a/include/ntfs-3g/misc.h b/include/ntfs-3g/misc.h new file mode 100755 index 0000000000000000000000000000000000000000..a03e964e83ca662d69c2acbc77f8bfdeb694b804 --- /dev/null +++ b/include/ntfs-3g/misc.h @@ -0,0 +1,30 @@ +/* + * misc.h : miscellaneous exports + * - memory allocation + * + * Copyright (c) 2008 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_MISC_H_ +#define _NTFS_MISC_H_ + +void *ntfs_calloc(size_t size); +void *ntfs_malloc(size_t size); + +#endif /* _NTFS_MISC_H_ */ + diff --git a/include/ntfs-3g/mst.h b/include/ntfs-3g/mst.h new file mode 100755 index 0000000000000000000000000000000000000000..d6ca6f27015df6a55e09f04787468323abfae9e5 --- /dev/null +++ b/include/ntfs-3g/mst.h @@ -0,0 +1,37 @@ +/* + * mst.h - Exports for multi sector transfer fixup functions. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_MST_H +#define _NTFS_MST_H + +#include "types.h" +#include "layout.h" +#include "volume.h" + +extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size); +extern int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, + BOOL warn); +extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size); +extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b); + +#endif /* defined _NTFS_MST_H */ + diff --git a/include/ntfs-3g/ntfstime.h b/include/ntfs-3g/ntfstime.h new file mode 100755 index 0000000000000000000000000000000000000000..d42d0c216cea3dfa9b6112de669f13eae652e49d --- /dev/null +++ b/include/ntfs-3g/ntfstime.h @@ -0,0 +1,133 @@ +/* + * ntfstime.h - NTFS time related functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_NTFSTIME_H +#define _NTFS_NTFSTIME_H + +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_GETTIMEOFDAY +#include <sys/time.h> +#endif + +#include "types.h" + +/* + * assume "struct timespec" is not defined if st_mtime is not defined + */ +#if !defined(st_mtime) & !defined(__timespec_defined) +#if 0 +struct timespec { + time_t tv_sec; + long tv_nsec; +} ; +#endif +#endif + +/* + * There are four times more conversions of internal representation + * to ntfs representation than any other conversion, so the most + * efficient internal representation is ntfs representation + * (with low endianness) + */ +typedef sle64 ntfs_time; + +#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000) + +/** + * ntfs2timespec - Convert an NTFS time to Unix time + * @ntfs_time: An NTFS time in 100ns units since 1601 + * + * NTFS stores times as the number of 100ns intervals since January 1st 1601 at + * 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD. + * + * Return: A Unix time (number of seconds since 1970, and nanoseconds) + */ +static __inline__ struct timespec ntfs2timespec(ntfs_time ntfstime) +{ + struct timespec spec; + s64 cputime; + + cputime = sle64_to_cpu(ntfstime); + spec.tv_sec = (cputime - (NTFS_TIME_OFFSET)) / 10000000; + spec.tv_nsec = (cputime - (NTFS_TIME_OFFSET) + - (s64)spec.tv_sec*10000000)*100; + /* force zero nsec for overflowing dates */ + if ((spec.tv_nsec < 0) || (spec.tv_nsec > 999999999)) + spec.tv_nsec = 0; + return (spec); +} + +/** + * timespec2ntfs - Convert Linux time to NTFS time + * @utc_time: Linux time to convert to NTFS + * + * Convert the Linux time @utc_time to its corresponding NTFS time. + * + * Linux stores time in a long at present and measures it as the number of + * 1-second intervals since 1st January 1970, 00:00:00 UTC + * with a separated non-negative nanosecond value + * + * NTFS uses Microsoft's standard time format which is stored in a sle64 and is + * measured as the number of 100 nano-second intervals since 1st January 1601, + * 00:00:00 UTC. + * + * Return: An NTFS time (100ns units since Jan 1601) + */ +static __inline__ ntfs_time timespec2ntfs(struct timespec spec) +{ + s64 units; + + units = (s64)spec.tv_sec * 10000000 + + NTFS_TIME_OFFSET + spec.tv_nsec/100; + return (cpu_to_le64(units)); +} + +/* + * Return the current time in ntfs format + */ + +static __inline__ ntfs_time ntfs_current_time(void) +{ + struct timespec now; + +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_SYS_CLOCK_GETTIME) + clock_gettime(CLOCK_REALTIME, &now); +#elif defined(HAVE_GETTIMEOFDAY) + struct timeval microseconds; + + gettimeofday(µseconds, (struct timezone*)NULL); + now.tv_sec = microseconds.tv_sec; + now.tv_nsec = microseconds.tv_usec*1000; +#else + now.tv_sec = time((time_t*)NULL); + now.tv_nsec = 0; +#endif + return (timespec2ntfs(now)); +} + +#endif /* _NTFS_NTFSTIME_H */ diff --git a/include/ntfs-3g/object_id.h b/include/ntfs-3g/object_id.h new file mode 100755 index 0000000000000000000000000000000000000000..31af9fd8a480428cdd6ed22ec4e0cfbbf6de475d --- /dev/null +++ b/include/ntfs-3g/object_id.h @@ -0,0 +1,35 @@ +/* + * + * Copyright (c) 2008 Jean-Pierre Andre + * + */ + +/* + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef OBJECT_ID_H +#define OBJECT_ID_H + +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_object_id(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_object_id(ntfs_inode *ni); + +int ntfs_delete_object_id_index(ntfs_inode *ni); + +#endif /* OBJECT_ID_H */ diff --git a/include/ntfs-3g/param.h b/include/ntfs-3g/param.h new file mode 100755 index 0000000000000000000000000000000000000000..da794abba17e2ec60a5519934a61ecf47cf4162b --- /dev/null +++ b/include/ntfs-3g/param.h @@ -0,0 +1,129 @@ +/* + * param.h - Parameter values for ntfs-3g + * + * Copyright (c) 2009-2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_PARAM_H +#define _NTFS_PARAM_H + +#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */ +#define CACHE_NIDATA_SIZE 64 /* idata cache, zero or >= 3 and not too big */ +#define CACHE_LOOKUP_SIZE 64 /* lookup cache, zero or >= 3 and not too big */ +#define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */ +#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */ + +#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */ +#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */ + + /* default security sub-authorities */ +enum { + DEFSECAUTH1 = -1153374643, /* 3141592653 */ + DEFSECAUTH2 = 589793238, + DEFSECAUTH3 = 462843383, + DEFSECBASE = 10000 +}; + +/* + * Parameters for compression + */ + + /* default option for compression */ +#define DEFAULT_COMPRESSION 1 + /* (log2 of) number of clusters in a compression block for new files */ +#define STANDARD_COMPRESSION_UNIT 4 + /* maximum cluster size for allowing compression for new files */ +#define MAX_COMPRESSION_CLUSTER_SIZE 4096 + +/* + * Parameters for default options + */ + +#define DEFAULT_DMTIME 60 /* default 1mn for delay_mtime */ + +/* + * Use of big write buffers + * + * With small volumes, the cluster allocator may fail to allocate + * enough clusters when the volume is nearly full. At most a run + * can be allocated per bitmap chunk. So, there is a danger when the + * number of chunks (capacity/(32768*clsiz)) is less than the number + * of clusters in the biggest write buffer (131072/clsiz). Hence + * a safe minimal capacity is 4GB + */ + +#define SAFE_CAPACITY_FOR_BIG_WRITES 0x100000000LL + +/* + * Parameters for runlists + */ + + /* only update the final extent of a runlist when appending data */ +#define PARTIAL_RUNLIST_UPDATING 1 + +/* + * Parameters for user and xattr mappings + */ + +#define XATTRMAPPINGFILE ".NTFS-3G/XattrMapping" /* default mapping file */ + +/* + * Parameters for path canonicalization + */ + +#define MAPPERNAMELTH 256 + +/* + * Permission checking modes for high level and low level + * + * The choices for high and low lowel are independent, they have + * no effect on the library + * + * Stick to the recommended values unless you understand the consequences + * on protection and performances. Use of cacheing is good for + * performances, but bad on security with internal fuse or external + * fuse older than 2.8 + * + * Possible values for high level : + * 1 : no cache, kernel control (recommended) + * 4 : no cache, file system control + * 7 : no cache, kernel control for ACLs + * + * Possible values for low level : + * 2 : no cache, kernel control + * 3 : use kernel/fuse cache, kernel control (external fuse >= 2.8) + * 5 : no cache, file system control (recommended) + * 8 : no cache, kernel control for ACLs + * + * Use of options 7 and 8 requires a patch to fuse + * When Posix ACLs are selected in the configure options, a value + * of 6 is added in the mount report. + */ + +#if defined(__sun) && defined(__SVR4) +#define HPERMSCONFIG 4 /* access control by kernel is broken on OpenIndiana */ +#else +#define HPERMSCONFIG 1 +#endif +#if defined(FUSE_INTERNAL) || !defined(FUSE_VERSION) || (FUSE_VERSION < 28) +#define LPERMSCONFIG 5 +#else +#define LPERMSCONFIG 3 +#endif + +#endif /* defined _NTFS_PARAM_H */ diff --git a/include/ntfs-3g/realpath.h b/include/ntfs-3g/realpath.h new file mode 100755 index 0000000000000000000000000000000000000000..970d2af8e8de181c87b31d56e55145cd623f535b --- /dev/null +++ b/include/ntfs-3g/realpath.h @@ -0,0 +1,24 @@ +/* + * realpath.h - realpath() aware of device mapper + */ + +#ifndef REALPATH_H +#define REALPATH_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_REALPATH +#define ntfs_realpath realpath +#else +extern char *ntfs_realpath(const char *path, char *resolved_path); +#endif + +#ifdef linux +extern char *ntfs_realpath_canonicalize(const char *path, char *resolved_path); +#else +#define ntfs_realpath_canonicalize ntfs_realpath +#endif + +#endif /* REALPATH_H */ diff --git a/include/ntfs-3g/reparse.h b/include/ntfs-3g/reparse.h new file mode 100755 index 0000000000000000000000000000000000000000..35f4aa456296b49fb66457ca9aec3535e86ae4d0 --- /dev/null +++ b/include/ntfs-3g/reparse.h @@ -0,0 +1,39 @@ +/* + * + * Copyright (c) 2008 Jean-Pierre Andre + * + */ + +/* + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef REPARSE_H +#define REPARSE_H + +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, + int *pattr_size); +BOOL ntfs_possible_symlink(ntfs_inode *ni); + +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni); + +int ntfs_delete_reparse_index(ntfs_inode *ni); + +#endif /* REPARSE_H */ diff --git a/include/ntfs-3g/runlist.h b/include/ntfs-3g/runlist.h new file mode 100755 index 0000000000000000000000000000000000000000..4b73af9f436359c51302c8a0bf4261ad61e4a2df --- /dev/null +++ b/include/ntfs-3g/runlist.h @@ -0,0 +1,90 @@ +/* + * runlist.h - Exports for runlist handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002 Anton Altaparmakov + * Copyright (c) 2002 Richard Russon + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_RUNLIST_H +#define _NTFS_RUNLIST_H + +#include "types.h" + +/* Forward declarations */ +typedef struct _runlist_element runlist_element; +typedef runlist_element runlist; + +#include "attrib.h" +#include "volume.h" + +/** + * struct _runlist_element - in memory vcn to lcn mapping array element. + * @vcn: starting vcn of the current array element + * @lcn: starting lcn of the current array element + * @length: length in clusters of the current array element + * + * The last vcn (in fact the last vcn + 1) is reached when length == 0. + * + * When lcn == -1 this means that the count vcns starting at vcn are not + * physically allocated (i.e. this is a hole / data is sparse). + */ +struct _runlist_element {/* In memory vcn to lcn mapping structure element. */ + VCN vcn; /* vcn = Starting virtual cluster number. */ + LCN lcn; /* lcn = Starting logical cluster number. */ + s64 length; /* Run length in clusters. */ +}; + +extern runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries); + +extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn); + +extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, + const s64 pos, s64 count, void *b); +extern s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, + s64 ofs, const s64 pos, s64 count, void *b); + +extern runlist_element *ntfs_runlists_merge(runlist_element *drl, + runlist_element *srl); + +extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl); + +extern int ntfs_get_nr_significant_bytes(const s64 n); + +extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, + const runlist_element *rl, const VCN start_vcn, int max_size); + +extern int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, + const s64 n); + +extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, + const int dst_len, const runlist_element *rl, + const VCN start_vcn, runlist_element const **stop_rl); + +extern int ntfs_rl_truncate(runlist **arl, const VCN start_vcn); + +extern int ntfs_rl_sparse(runlist *rl); +extern s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl); + +#ifdef NTFS_TEST +int test_rl_main(int argc, char *argv[]); +#endif + +#endif /* defined _NTFS_RUNLIST_H */ + diff --git a/include/ntfs-3g/security.h b/include/ntfs-3g/security.h new file mode 100755 index 0000000000000000000000000000000000000000..8875c9c14f86a9d5f85a0dc125948f6fa430ce42 --- /dev/null +++ b/include/ntfs-3g/security.h @@ -0,0 +1,363 @@ +/* + * security.h - Exports for handling security/ACLs in NTFS. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2007-2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_SECURITY_H +#define _NTFS_SECURITY_H + +#include "types.h" +#include "layout.h" +#include "inode.h" +#include "dir.h" + +#ifndef POSIXACLS +#define POSIXACLS 0 +#endif + +typedef u16 be16; +typedef u32 be32; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define const_cpu_to_be16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L)) +#define const_cpu_to_be32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \ + + (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L)) +#else +#define const_cpu_to_be16(x) (x) +#define const_cpu_to_be32(x) (x) +#endif + +/* + * item in the mapping list + */ + +struct MAPPING { + struct MAPPING *next; + int xid; /* linux id : uid or gid */ + SID *sid; /* Windows id : usid or gsid */ + int grcnt; /* group count (for users only) */ + gid_t *groups; /* groups which the user is member of */ +}; + +/* + * Entry in the permissions cache + * Note : this cache is not organized as a generic cache + */ + +struct CACHED_PERMISSIONS { + uid_t uid; + gid_t gid; + le32 inh_fileid; + le32 inh_dirid; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; + unsigned int pxdescsize:16; +#endif + unsigned int mode:12; + unsigned int valid:1; +} ; + +/* + * Entry in the permissions cache for directories with no security_id + */ + +struct CACHED_PERMISSIONS_LEGACY { + struct CACHED_PERMISSIONS_LEGACY *next; + struct CACHED_PERMISSIONS_LEGACY *previous; + void *variable; + size_t varsize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + u64 mft_no; + struct CACHED_PERMISSIONS perm; +} ; + +/* + * Entry in the securid cache + */ + +struct CACHED_SECURID { + struct CACHED_SECURID *next; + struct CACHED_SECURID *previous; + void *variable; + size_t varsize; + union ALIGNMENT payload[0]; + /* above fields must match "struct CACHED_GENERIC" */ + uid_t uid; + gid_t gid; + unsigned int dmode; + le32 securid; +} ; + +/* + * Header of the security cache + * (has no cache structure by itself) + */ + +struct CACHED_PERMISSIONS_HEADER { + unsigned int last; + /* statistics for permissions */ + unsigned long p_writes; + unsigned long p_reads; + unsigned long p_hits; +} ; + +/* + * The whole permissions cache + */ + +struct PERMISSIONS_CACHE { + struct CACHED_PERMISSIONS_HEADER head; + struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */ +} ; + +/* + * Security flags values + */ + +enum { + SECURITY_DEFAULT, /* rely on fuse for permissions checking */ + SECURITY_RAW, /* force same ownership/permissions on files */ + SECURITY_ACL, /* enable Posix ACLs (when compiled in) */ + SECURITY_ADDSECURIDS, /* upgrade old security descriptors */ + SECURITY_STATICGRPS, /* use static groups for access control */ + SECURITY_WANTED /* a security related option was present */ +} ; + +/* + * Security context, needed by most security functions + */ + +enum { MAPUSERS, MAPGROUPS, MAPCOUNT } ; + +struct SECURITY_CONTEXT { + ntfs_volume *vol; + struct MAPPING *mapping[MAPCOUNT]; + struct PERMISSIONS_CACHE **pseccache; + uid_t uid; /* uid of user requesting (not the mounter) */ + gid_t gid; /* gid of user requesting (not the mounter) */ + pid_t tid; /* thread id of thread requesting */ + mode_t umask; /* umask of requesting thread */ + } ; + +#if POSIXACLS + +/* + * Posix ACL structures + */ + +struct POSIX_ACE { + u16 tag; + u16 perms; + s32 id; +} __attribute__((__packed__)); + +struct POSIX_ACL { + u8 version; + u8 flags; + u16 filler; + struct POSIX_ACE ace[0]; +} __attribute__((__packed__)); + +struct POSIX_SECURITY { + mode_t mode; + int acccnt; + int defcnt; + int firstdef; + u16 tagsset; + s32 alignment[0]; + struct POSIX_ACL acl; +} ; + +/* + * Posix tags, cpu-endian 16 bits + */ + +enum { + POSIX_ACL_USER_OBJ = 1, + POSIX_ACL_USER = 2, + POSIX_ACL_GROUP_OBJ = 4, + POSIX_ACL_GROUP = 8, + POSIX_ACL_MASK = 16, + POSIX_ACL_OTHER = 32, + POSIX_ACL_SPECIAL = 64 /* internal use only */ +} ; + +#define POSIX_ACL_EXTENSIONS (POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK) + +/* + * Posix permissions, cpu-endian 16 bits + */ + +enum { + POSIX_PERM_X = 1, + POSIX_PERM_W = 2, + POSIX_PERM_R = 4, + POSIX_PERM_DENIAL = 64 /* internal use only */ +} ; + +#define POSIX_VERSION 2 + +#endif + +extern BOOL ntfs_guid_is_zero(const GUID *guid); +extern char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str); + +/** + * ntfs_sid_is_valid - determine if a SID is valid + * @sid: SID for which to determine if it is valid + * + * Determine if the SID pointed to by @sid is valid. + * + * Return TRUE if it is valid and FALSE otherwise. + */ +static __inline__ BOOL ntfs_sid_is_valid(const SID *sid) +{ + if (!sid || sid->revision != SID_REVISION || + sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES) + return FALSE; + return TRUE; +} + +extern int ntfs_sid_to_mbs_size(const SID *sid); +extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, + size_t sid_str_size); +extern void ntfs_generate_guid(GUID *guid); +extern int ntfs_sd_add_everyone(ntfs_inode *ni); + +extern le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, + const u32 len); + +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef); +int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, struct stat*); +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode); +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni); +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, int accesstype); +int ntfs_allowed_create(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, gid_t *pgid, mode_t *pdsetgid); +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, + const char *path, int accesstype); + +#if POSIXACLS +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir); +#else +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, mode_t mode, BOOL isdir); +#endif +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid); +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); +#if POSIXACLS +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + mode_t mode, struct POSIX_SECURITY *pxdesc); +#else +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); +#endif +le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, BOOL fordir); +int ntfs_open_secure(ntfs_volume *vol); +void ntfs_close_secure(struct SECURITY_CONTEXT *scx); + +#if POSIXACLS + +int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + ntfs_inode *dir_ni, mode_t mode); +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size); +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, const char *value, size_t size, + int flags); +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name); +#endif + +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size); +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags); + +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size); +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags); + + +/* + * Security API for direct access to security descriptors + * based on Win32 API + */ + +#define MAGIC_API 0x09042009 + +struct SECURITY_API { + u32 magic; + struct SECURITY_CONTEXT security; + struct PERMISSIONS_CACHE *seccache; +} ; + +/* + * The following constants are used in interfacing external programs. + * They are not to be stored on disk and must be defined in their + * native cpu representation. + * When disk representation (le) is needed, use SE_DACL_PRESENT, etc. + */ +enum { OWNER_SECURITY_INFORMATION = 1, + GROUP_SECURITY_INFORMATION = 2, + DACL_SECURITY_INFORMATION = 4, + SACL_SECURITY_INFORMATION = 8 +} ; + +int ntfs_get_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, + char *buf, u32 buflen, u32 *psize); +int ntfs_set_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, const char *attr); +int ntfs_get_file_attributes(struct SECURITY_API *scapi, + const char *path); +BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, + const char *path, s32 attrib); +BOOL ntfs_read_directory(struct SECURITY_API *scapi, + const char *path, ntfs_filldir_t callback, void *context); +int ntfs_read_sds(struct SECURITY_API *scapi, + char *buf, u32 size, u32 offset); +INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi, + INDEX_ENTRY *entry); +INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi, + INDEX_ENTRY *entry); +struct SECURITY_API *ntfs_initialize_file_security(const char *device, + unsigned long flags); +BOOL ntfs_leave_file_security(struct SECURITY_API *scx); + +int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf); +int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf); +int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid); +int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid); + +#endif /* defined _NTFS_SECURITY_H */ diff --git a/include/ntfs-3g/support.h b/include/ntfs-3g/support.h new file mode 100755 index 0000000000000000000000000000000000000000..6af4761e22b2e03d36f0b376c9b47746240678c3 --- /dev/null +++ b/include/ntfs-3g/support.h @@ -0,0 +1,85 @@ +/* + * support.h - Useful definitions and macros. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_SUPPORT_H +#define _NTFS_SUPPORT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDDEF_H +#include <stddef.h> +#endif + +/* + * Our mailing list. Use this define to prevent typos in email address. + */ +#define NTFS_DEV_LIST "ntfs-3g-devel@lists.sf.net" + +/* + * Generic macro to convert pointers to values for comparison purposes. + */ +#ifndef p2n +#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p))) +#endif + +/* + * The classic min and max macros. + */ +#ifndef min +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#endif + +#ifndef max +#define max(a,b) ((a) >= (b) ? (a) : (b)) +#endif + +/* + * Useful macro for determining the offset of a struct member. + */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/* + * Simple bit operation macros. NOTE: These are NOT atomic. + */ +#define test_bit(bit, var) ((var) & (1 << (bit))) +#define set_bit(bit, var) (var) |= 1 << (bit) +#define clear_bit(bit, var) (var) &= ~(1 << (bit)) + +#define test_and_set_bit(bit, var) \ +({ \ + const BOOL old_state = test_bit(bit, var); \ + set_bit(bit, var); \ + old_state; \ +}) + +#define test_and_clear_bit(bit, var) \ +({ \ + const BOOL old_state = test_bit(bit, var); \ + clear_bit(bit, var); \ + old_state; \ +}) + +#endif /* defined _NTFS_SUPPORT_H */ + diff --git a/include/ntfs-3g/types.h b/include/ntfs-3g/types.h new file mode 100755 index 0000000000000000000000000000000000000000..a64f12faaea4dd15a40f0b8dcca3671a774fa475 --- /dev/null +++ b/include/ntfs-3g/types.h @@ -0,0 +1,132 @@ +/* + * types.h - Misc type definitions not related to on-disk structure. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_TYPES_H +#define _NTFS_TYPES_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_STDINT_H || !HAVE_CONFIG_H +#include <stdint.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +typedef uint8_t u8; /* Unsigned types of an exact size */ +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; /* Signed types of an exact size */ +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +typedef u16 le16; +typedef u32 le32; +typedef u64 le64; + +/* + * Declare sle{16,32,64} to be unsigned because we do not want sign extension + * on BE architectures. + */ +typedef u16 sle16; +typedef u32 sle32; +typedef u64 sle64; + +typedef u16 ntfschar; /* 2-byte Unicode character type. */ +#define UCHAR_T_SIZE_BITS 1 + +/* + * Clusters are signed 64-bit values on NTFS volumes. We define two types, LCN + * and VCN, to allow for type checking and better code readability. + */ +typedef s64 VCN; +typedef sle64 leVCN; +typedef s64 LCN; +typedef sle64 leLCN; + +/* + * The NTFS journal $LogFile uses log sequence numbers which are signed 64-bit + * values. We define our own type LSN, to allow for type checking and better + * code readability. + */ +typedef s64 LSN; +typedef sle64 leLSN; + +/* + * Cygwin has a collision between our BOOL and <windef.h>'s + * As long as this file will be included after <windows.h> were fine. + */ +#ifndef _WINDEF_H +/** + * enum BOOL - These are just to make the code more readable... + */ +typedef enum { +#ifndef FALSE + FALSE = 0, +#endif +#ifndef NO + NO = 0, +#endif +#ifndef ZERO + ZERO = 0, +#endif +#ifndef TRUE + TRUE = 1, +#endif +#ifndef YES + YES = 1, +#endif +#ifndef ONE + ONE = 1, +#endif +} BOOL; +#endif /* defined _WINDEF_H */ + +/** + * enum IGNORE_CASE_BOOL - + */ +typedef enum { + CASE_SENSITIVE = 0, + IGNORE_CASE = 1, +} IGNORE_CASE_BOOL; + +#define STATUS_OK (0) +#define STATUS_ERROR (-1) +#define STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT (-2) +#define STATUS_KEEP_SEARCHING (-3) +#define STATUS_NOT_FOUND (-4) + +/* + * Force alignment in a struct if required by processor + */ +union ALIGNMENT { + u64 u64align; + void *ptralign; +} ; + +#endif /* defined _NTFS_TYPES_H */ + diff --git a/include/ntfs-3g/unistr.h b/include/ntfs-3g/unistr.h new file mode 100755 index 0000000000000000000000000000000000000000..b6d428e38dcc9fefe7a7cc2211b2f65b87105028 --- /dev/null +++ b/include/ntfs-3g/unistr.h @@ -0,0 +1,121 @@ +/* + * unistr.h - Exports for Unicode string handling. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_UNISTR_H +#define _NTFS_UNISTR_H + +#include "types.h" +#include "layout.h" + +extern BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, + const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_size); + +extern int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, + const ntfschar *name2, const u32 name2_len, + const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_len); + +extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n); + +extern int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, + const ntfschar *upcase, const u32 upcase_size); + +extern u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen); + +extern ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen); + +extern void ntfs_name_upcase(ntfschar *name, u32 name_len, + const ntfschar *upcase, const u32 upcase_len); + +extern void ntfs_name_locase(ntfschar *name, u32 name_len, + const ntfschar *locase, const u32 locase_len); + +extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, + const ntfschar *upcase, const u32 upcase_len); + +extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, + int outs_len); +extern int ntfs_mbstoucs(const char *ins, ntfschar **outs); + +extern char *ntfs_uppercase_mbs(const char *low, + const ntfschar *upcase, u32 upcase_len); + +extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len); +extern u32 ntfs_upcase_build_default(ntfschar **upcase); +extern ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt); + +extern ntfschar *ntfs_str2ucs(const char *s, int *len); + +extern void ntfs_ucsfree(ntfschar *ucs); + +extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len); +extern BOOL ntfs_forbidden_names(ntfs_volume *vol, + const ntfschar *name, int len); +extern BOOL ntfs_collapsible_chars(ntfs_volume *vol, + const ntfschar *shortname, int shortlen, + const ntfschar *longname, int longlen); + +extern int ntfs_set_char_encoding(const char *locale); + +#if defined(__APPLE__) || defined(__DARWIN__) +/** + * Mac OS X only. + * + * Sets file name Unicode normalization form conversion on or off. + * normalize=0 : Off + * normalize=1 : On + * If set to on, all filenames returned by ntfs-3g will be converted to the NFD + * normalization form, while all filenames recieved by ntfs-3g will be converted to the NFC + * normalization form. Since Windows and most other OS:es use the NFC form while Mac OS X + * mostly uses NFD, this conversion increases compatibility between Mac applications and + * NTFS-3G. + * + * @param normalize decides whether or not the string functions will do automatic filename + * normalization when converting to and from UTF-8. 0 means normalization is disabled, + * 1 means it is enabled. + * @return -1 if the argument was invalid or an error occurred, 0 if all went well. + */ +extern int ntfs_macosx_normalize_filenames(int normalize); + +/** + * Mac OS X only. + * + * Normalizes the input string "utf8_string" to one of the normalization forms NFD or NFC. + * The parameter "composed" decides whether output should be in composed, NFC, form + * (composed == 1) or decomposed, NFD, form (composed == 0). + * Input is assumed to be properly UTF-8 encoded and null-terminated. Output will be a newly + * ntfs_calloc'ed string encoded in UTF-8. It is the callers responsibility to free(...) the + * allocated string when it's no longer needed. + * + * @param utf8_string the input string, which may be in any normalization form. + * @param target a pointer where the resulting string will be stored. + * @param composed decides which composition form to normalize the input string to. 0 means + * composed form (NFC), 1 means decomposed form (NFD). + * @return -1 if the normalization failed for some reason, otherwise the length of the + * normalized string stored in target. + */ +extern int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, int composed); +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + +#endif /* defined _NTFS_UNISTR_H */ + diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h new file mode 100755 index 0000000000000000000000000000000000000000..2720864c286ef320dcb8eab6adaab582807cdd6a --- /dev/null +++ b/include/ntfs-3g/volume.h @@ -0,0 +1,311 @@ +/* + * volume.h - Exports for NTFS volume handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2005-2009 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_VOLUME_H +#define _NTFS_VOLUME_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + /* Do not #include <sys/mount.h> here : conflicts with <linux/fs.h> */ +#ifdef HAVE_MNTENT_H +#include <mntent.h> +#endif + +/* Forward declaration */ +typedef struct _ntfs_volume ntfs_volume; + +#include "param.h" +#include "types.h" +#include "support.h" +#include "device.h" +#include "inode.h" +#include "attrib.h" +#include "index.h" + +/** + * enum ntfs_mount_flags - + * + * Flags for the ntfs_mount() function. + */ +enum { + NTFS_MNT_NONE = 0x00000000, + NTFS_MNT_RDONLY = 0x00000001, + NTFS_MNT_FORENSIC = 0x04000000, /* No modification during + * mount. */ + NTFS_MNT_EXCLUSIVE = 0x08000000, + NTFS_MNT_RECOVER = 0x10000000, + NTFS_MNT_IGNORE_HIBERFILE = 0x20000000, +}; +typedef unsigned long ntfs_mount_flags; + +/** + * enum ntfs_mounted_flags - + * + * Flags returned by the ntfs_check_if_mounted() function. + */ +typedef enum { + NTFS_MF_MOUNTED = 1, /* Device is mounted. */ + NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */ + NTFS_MF_READONLY = 4, /* Device is mounted read-only. */ +} ntfs_mounted_flags; + +extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags); + +typedef enum { + NTFS_VOLUME_OK = 0, + NTFS_VOLUME_SYNTAX_ERROR = 11, + NTFS_VOLUME_NOT_NTFS = 12, + NTFS_VOLUME_CORRUPT = 13, + NTFS_VOLUME_HIBERNATED = 14, + NTFS_VOLUME_UNCLEAN_UNMOUNT = 15, + NTFS_VOLUME_LOCKED = 16, + NTFS_VOLUME_RAID = 17, + NTFS_VOLUME_UNKNOWN_REASON = 18, + NTFS_VOLUME_NO_PRIVILEGE = 19, + NTFS_VOLUME_OUT_OF_MEMORY = 20, + NTFS_VOLUME_FUSE_ERROR = 21, + NTFS_VOLUME_INSECURE = 22 +} ntfs_volume_status; + +/** + * enum ntfs_volume_state_bits - + * + * Defined bits for the state field in the ntfs_volume structure. + */ +typedef enum { + NV_ReadOnly, /* 1: Volume is read-only. */ + NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */ + NV_LogFileEmpty, /* 1: $logFile journal is empty. */ + NV_ShowSysFiles, /* 1: Show NTFS metafiles. */ + NV_ShowHidFiles, /* 1: Show files marked hidden. */ + NV_HideDotFiles, /* 1: Set hidden flag on dot files */ + NV_Compression, /* 1: allow compression */ + NV_NoFixupWarn, /* 1: Do not log fixup errors */ +} ntfs_volume_state_bits; + +#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) +#define set_nvol_flag(nv, flag) set_bit(NV_##flag, (nv)->state) +#define clear_nvol_flag(nv, flag) clear_bit(NV_##flag, (nv)->state) + +#define NVolReadOnly(nv) test_nvol_flag(nv, ReadOnly) +#define NVolSetReadOnly(nv) set_nvol_flag(nv, ReadOnly) +#define NVolClearReadOnly(nv) clear_nvol_flag(nv, ReadOnly) + +#define NVolCaseSensitive(nv) test_nvol_flag(nv, CaseSensitive) +#define NVolSetCaseSensitive(nv) set_nvol_flag(nv, CaseSensitive) +#define NVolClearCaseSensitive(nv) clear_nvol_flag(nv, CaseSensitive) + +#define NVolLogFileEmpty(nv) test_nvol_flag(nv, LogFileEmpty) +#define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty) +#define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty) + +#define NVolShowSysFiles(nv) test_nvol_flag(nv, ShowSysFiles) +#define NVolSetShowSysFiles(nv) set_nvol_flag(nv, ShowSysFiles) +#define NVolClearShowSysFiles(nv) clear_nvol_flag(nv, ShowSysFiles) + +#define NVolShowHidFiles(nv) test_nvol_flag(nv, ShowHidFiles) +#define NVolSetShowHidFiles(nv) set_nvol_flag(nv, ShowHidFiles) +#define NVolClearShowHidFiles(nv) clear_nvol_flag(nv, ShowHidFiles) + +#define NVolHideDotFiles(nv) test_nvol_flag(nv, HideDotFiles) +#define NVolSetHideDotFiles(nv) set_nvol_flag(nv, HideDotFiles) +#define NVolClearHideDotFiles(nv) clear_nvol_flag(nv, HideDotFiles) + +#define NVolCompression(nv) test_nvol_flag(nv, Compression) +#define NVolSetCompression(nv) set_nvol_flag(nv, Compression) +#define NVolClearCompression(nv) clear_nvol_flag(nv, Compression) + +#define NVolNoFixupWarn(nv) test_nvol_flag(nv, NoFixupWarn) +#define NVolSetNoFixupWarn(nv) set_nvol_flag(nv, NoFixupWarn) +#define NVolClearNoFixupWarn(nv) clear_nvol_flag(nv, NoFixupWarn) + +/* + * NTFS version 1.1 and 1.2 are used by Windows NT4. + * NTFS version 2.x is used by Windows 2000 Beta + * NTFS version 3.0 is used by Windows 2000. + * NTFS version 3.1 is used by Windows XP, 2003 and Vista. + */ + +#define NTFS_V1_1(major, minor) ((major) == 1 && (minor) == 1) +#define NTFS_V1_2(major, minor) ((major) == 1 && (minor) == 2) +#define NTFS_V2_X(major, minor) ((major) == 2) +#define NTFS_V3_0(major, minor) ((major) == 3 && (minor) == 0) +#define NTFS_V3_1(major, minor) ((major) == 3 && (minor) == 1) + +#define NTFS_BUF_SIZE 8192 + +/** + * struct _ntfs_volume - structure describing an open volume in memory. + */ +struct _ntfs_volume { + union { + struct ntfs_device *dev; /* NTFS device associated with + the volume. */ + void *sb; /* For kernel porting compatibility. */ + }; + char *vol_name; /* Name of the volume. */ + unsigned long state; /* NTFS specific flags describing this volume. + See ntfs_volume_state_bits above. */ + + ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */ + u8 major_ver; /* Ntfs major version of volume. */ + u8 minor_ver; /* Ntfs minor version of volume. */ + le16 flags; /* Bit array of VOLUME_* flags. */ + + u16 sector_size; /* Byte size of a sector. */ + u8 sector_size_bits; /* Log(2) of the byte size of a sector. */ + u32 cluster_size; /* Byte size of a cluster. */ + u32 mft_record_size; /* Byte size of a mft record. */ + u32 indx_record_size; /* Byte size of a INDX record. */ + u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */ + u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */ + u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */ + + /* Variables used by the cluster and mft allocators. */ + u8 mft_zone_multiplier; /* Initial mft zone multiplier. */ + u8 full_zones; /* cluster zones which are full */ + s64 mft_data_pos; /* Mft record number at which to allocate the + next mft record. */ + LCN mft_zone_start; /* First cluster of the mft zone. */ + LCN mft_zone_end; /* First cluster beyond the mft zone. */ + LCN mft_zone_pos; /* Current position in the mft zone. */ + LCN data1_zone_pos; /* Current position in the first data zone. */ + LCN data2_zone_pos; /* Current position in the second data zone. */ + + s64 nr_clusters; /* Volume size in clusters, hence also the + number of bits in lcn_bitmap. */ + ntfs_inode *lcnbmp_ni; /* ntfs_inode structure for FILE_Bitmap. */ + ntfs_attr *lcnbmp_na; /* ntfs_attr structure for the data attribute + of FILE_Bitmap. Each bit represents a + cluster on the volume, bit 0 representing + lcn 0 and so on. A set bit means that the + cluster and vice versa. */ + + LCN mft_lcn; /* Logical cluster number of the data attribute + for FILE_MFT. */ + ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */ + ntfs_attr *mft_na; /* ntfs_attr structure for the data attribute + of FILE_MFT. */ + ntfs_attr *mftbmp_na; /* ntfs_attr structure for the bitmap attribute + of FILE_MFT. Each bit represents an mft + record in the $DATA attribute, bit 0 + representing mft record 0 and so on. A set + bit means that the mft record is in use and + vice versa. */ + + ntfs_inode *secure_ni; /* ntfs_inode structure for FILE $Secure */ + ntfs_index_context *secure_xsii; /* index for using $Secure:$SII */ + ntfs_index_context *secure_xsdh; /* index for using $Secure:$SDH */ + int secure_reentry; /* check for non-rentries */ + unsigned int secure_flags; /* flags, see security.h for values */ + + int mftmirr_size; /* Size of the FILE_MFTMirr in mft records. */ + LCN mftmirr_lcn; /* Logical cluster number of the data attribute + for FILE_MFTMirr. */ + ntfs_inode *mftmirr_ni; /* ntfs_inode structure for FILE_MFTMirr. */ + ntfs_attr *mftmirr_na; /* ntfs_attr structure for the data attribute + of FILE_MFTMirr. */ + + ntfschar *upcase; /* Upper case equivalents of all 65536 2-byte + Unicode characters. Obtained from + FILE_UpCase. */ + u32 upcase_len; /* Length in Unicode characters of the upcase + table. */ + ntfschar *locase; /* Lower case equivalents of all 65536 2-byte + Unicode characters. Only if option + case_ignore is set. */ + + ATTR_DEF *attrdef; /* Attribute definitions. Obtained from + FILE_AttrDef. */ + s32 attrdef_len; /* Size of the attribute definition table in + bytes. */ + + s64 free_clusters; /* Track the number of free clusters which + greatly improves statfs() performance */ + s64 free_mft_records; /* Same for free mft records (see above) */ + BOOL efs_raw; /* volume is mounted for raw access to + efs-encrypted files */ +#ifdef XATTR_MAPPINGS + struct XATTRMAPPING *xattr_mapping; +#endif /* XATTR_MAPPINGS */ +#if CACHE_INODE_SIZE + struct CACHE_HEADER *xinode_cache; +#endif +#if CACHE_NIDATA_SIZE + struct CACHE_HEADER *nidata_cache; +#endif +#if CACHE_LOOKUP_SIZE + struct CACHE_HEADER *lookup_cache; +#endif +#if CACHE_SECURID_SIZE + struct CACHE_HEADER *securid_cache; +#endif +#if CACHE_LEGACY_SIZE + struct CACHE_HEADER *legacy_cache; +#endif + +}; + +extern const char *ntfs_home; + +extern ntfs_volume *ntfs_volume_alloc(void); + +extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, + ntfs_mount_flags flags); + +extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, + ntfs_mount_flags flags); + +extern ntfs_volume *ntfs_mount(const char *name, ntfs_mount_flags flags); +extern int ntfs_umount(ntfs_volume *vol, const BOOL force); + +extern int ntfs_version_is_supported(ntfs_volume *vol); +extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose); +extern int ntfs_logfile_reset(ntfs_volume *vol); + +extern int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags); + +extern int ntfs_volume_error(int err); +extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err); + +extern int ntfs_volume_get_free_space(ntfs_volume *vol); +extern int ntfs_volume_rename(ntfs_volume *vol, const ntfschar *label, + int label_len); + +extern int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files); +extern int ntfs_set_locale(void); +extern int ntfs_set_ignore_case(ntfs_volume *vol); + +#endif /* defined _NTFS_VOLUME_H */ + diff --git a/include/ntfs-3g/xattrs.h b/include/ntfs-3g/xattrs.h new file mode 100755 index 0000000000000000000000000000000000000000..d4e43a3e74b66c77baa334ffa2609631685571f7 --- /dev/null +++ b/include/ntfs-3g/xattrs.h @@ -0,0 +1,76 @@ +/* + * xattrs.h : definitions related to system extended attributes + * + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_XATTR_H_ +#define _NTFS_XATTR_H_ + +/* + * Identification of data mapped to the system name space + */ + +enum SYSTEMXATTRS { + XATTR_UNMAPPED, + XATTR_NTFS_ACL, + XATTR_NTFS_ATTRIB, + XATTR_NTFS_ATTRIB_BE, + XATTR_NTFS_EFSINFO, + XATTR_NTFS_REPARSE_DATA, + XATTR_NTFS_OBJECT_ID, + XATTR_NTFS_DOS_NAME, + XATTR_NTFS_TIMES, + XATTR_NTFS_TIMES_BE, + XATTR_NTFS_CRTIME, + XATTR_NTFS_CRTIME_BE, + XATTR_NTFS_EA, + XATTR_POSIX_ACC, + XATTR_POSIX_DEF +} ; + +struct XATTRMAPPING { + struct XATTRMAPPING *next; + enum SYSTEMXATTRS xattr; + char name[1]; /* variable length */ +} ; + +#ifdef XATTR_MAPPINGS + +struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol, + const char *path); +void ntfs_xattr_free_mapping(struct XATTRMAPPING*); + +#endif /* XATTR_MAPPINGS */ + +enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name, + ntfs_volume *vol); + +int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size); +int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags); +int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni); + +#endif /* _NTFS_XATTR_H_ */ diff --git a/install-sh b/install-sh new file mode 100755 index 0000000000000000000000000000000000000000..377bb8687ffe16bfc79ea25c8667cabf72aaf2c2 --- /dev/null +++ b/install-sh @@ -0,0 +1,527 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2011-11-20.07; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/libfuse-lite/Android.mk b/libfuse-lite/Android.mk new file mode 100755 index 0000000000000000000000000000000000000000..1c89247c666790525f4d8627b9fcb5e31db7effc --- /dev/null +++ b/libfuse-lite/Android.mk @@ -0,0 +1,38 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + fuse.c \ + fuse_i.h \ + fuse_kern_chan.c \ + fuse_loop.c \ + fuse_lowlevel.c \ + fuse_misc.h \ + fuse_opt.c \ + fuse_session.c \ + fuse_signals.c \ + fusermount.c \ + helper.c \ + mount.c \ + mount_util.c \ + mount_util.h + +LOCAL_MODULE := libfuse-lite +LOCAL_MODULE_TAGS := optional + +LOCAL_SYSTEM_SHARED_LIBRARIES := \ + libc + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/../include/fuse-lite + +LOCAL_CFLAGS := -O2 -g -W -Wall \ + -D_LARGEFILE_SOURCE \ + -D_FILE_OFFSET_BITS=64 \ + -DHAVE_CONFIG_H + +LOCAL_PRELINK_MODULE := false + +include $(BUILD_STATIC_LIBRARY) + diff --git a/libfuse-lite/Makefile.am b/libfuse-lite/Makefile.am new file mode 100755 index 0000000000000000000000000000000000000000..d9591ec47fe376649637acb2d24dc238a2f1a17a --- /dev/null +++ b/libfuse-lite/Makefile.am @@ -0,0 +1,31 @@ + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in + +if FUSE_INTERNAL +noinst_LTLIBRARIES = libfuse-lite.la +endif + +libfuse_lite_la_CFLAGS= \ + $(AM_CFLAGS) \ + $(LIBFUSE_LITE_CFLAGS) \ + -I$(top_srcdir)/include/fuse-lite + +libfuse_lite_la_LIBADD = $(LIBFUSE_LITE_LIBS) + +libfuse_lite_la_SOURCES = \ + fuse.c \ + fuse_i.h \ + fuse_kern_chan.c \ + fuse_loop.c \ + fuse_lowlevel.c \ + fuse_misc.h \ + fuse_opt.c \ + fuse_session.c \ + fuse_signals.c \ + fusermount.c \ + helper.c \ + mount.c \ + mount_util.c \ + mount_util.h + +libs: libfuse-lite.la diff --git a/libfuse-lite/Makefile.in b/libfuse-lite/Makefile.in new file mode 100755 index 0000000000000000000000000000000000000000..886b46a23513fc827dafe346e01f2f2dc792cfc3 --- /dev/null +++ b/libfuse-lite/Makefile.in @@ -0,0 +1,729 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = libfuse-lite +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libfuse_lite_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_libfuse_lite_la_OBJECTS = libfuse_lite_la-fuse.lo \ + libfuse_lite_la-fuse_kern_chan.lo libfuse_lite_la-fuse_loop.lo \ + libfuse_lite_la-fuse_lowlevel.lo libfuse_lite_la-fuse_opt.lo \ + libfuse_lite_la-fuse_session.lo \ + libfuse_lite_la-fuse_signals.lo libfuse_lite_la-fusermount.lo \ + libfuse_lite_la-helper.lo libfuse_lite_la-mount.lo \ + libfuse_lite_la-mount_util.lo +libfuse_lite_la_OBJECTS = $(am_libfuse_lite_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libfuse_lite_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(libfuse_lite_la_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +@FUSE_INTERNAL_TRUE@am_libfuse_lite_la_rpath = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libfuse_lite_la_SOURCES) +DIST_SOURCES = $(libfuse_lite_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FUSE_MODULE_CFLAGS = @FUSE_MODULE_CFLAGS@ +FUSE_MODULE_LIBS = @FUSE_MODULE_LIBS@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDCONFIG = @LDCONFIG@ +LDFLAGS = @LDFLAGS@ +LIBFUSE_LITE_CFLAGS = @LIBFUSE_LITE_CFLAGS@ +LIBFUSE_LITE_LIBS = @LIBFUSE_LITE_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBNTFS_3G_VERSION = @LIBNTFS_3G_VERSION@ +LIBNTFS_CPPFLAGS = @LIBNTFS_CPPFLAGS@ +LIBNTFS_LIBS = @LIBNTFS_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MKNTFS_CPPFLAGS = @MKNTFS_CPPFLAGS@ +MKNTFS_LIBS = @MKNTFS_LIBS@ +MV = @MV@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NTFSPROGS_STATIC_LIBS = @NTFSPROGS_STATIC_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OUTPUT_FORMAT = @OUTPUT_FORMAT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +RM = @RM@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +all_includes = @all_includes@ +all_libraries = @all_libraries@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +ntfs3gincludedir = @ntfs3gincludedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rootbindir = @rootbindir@ +rootlibdir = @rootlibdir@ +rootsbindir = @rootsbindir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +@FUSE_INTERNAL_TRUE@noinst_LTLIBRARIES = libfuse-lite.la +libfuse_lite_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBFUSE_LITE_CFLAGS) \ + -I$(top_srcdir)/include/fuse-lite + +libfuse_lite_la_LIBADD = $(LIBFUSE_LITE_LIBS) +libfuse_lite_la_SOURCES = \ + fuse.c \ + fuse_i.h \ + fuse_kern_chan.c \ + fuse_loop.c \ + fuse_lowlevel.c \ + fuse_misc.h \ + fuse_opt.c \ + fuse_session.c \ + fuse_signals.c \ + fusermount.c \ + helper.c \ + mount.c \ + mount_util.c \ + mount_util.h + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libfuse-lite/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu libfuse-lite/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libfuse-lite.la: $(libfuse_lite_la_OBJECTS) $(libfuse_lite_la_DEPENDENCIES) $(EXTRA_libfuse_lite_la_DEPENDENCIES) + $(AM_V_CCLD)$(libfuse_lite_la_LINK) $(am_libfuse_lite_la_rpath) $(libfuse_lite_la_OBJECTS) $(libfuse_lite_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfuse_lite_la-fuse.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfuse_lite_la-fuse_kern_chan.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfuse_lite_la-fuse_loop.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfuse_lite_la-fuse_lowlevel.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfuse_lite_la-fuse_opt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfuse_lite_la-fuse_session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfuse_lite_la-fuse_signals.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfuse_lite_la-fusermount.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfuse_lite_la-helper.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfuse_lite_la-mount.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfuse_lite_la-mount_util.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libfuse_lite_la-fuse.lo: fuse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -MT libfuse_lite_la-fuse.lo -MD -MP -MF $(DEPDIR)/libfuse_lite_la-fuse.Tpo -c -o libfuse_lite_la-fuse.lo `test -f 'fuse.c' || echo '$(srcdir)/'`fuse.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfuse_lite_la-fuse.Tpo $(DEPDIR)/libfuse_lite_la-fuse.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuse.c' object='libfuse_lite_la-fuse.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -c -o libfuse_lite_la-fuse.lo `test -f 'fuse.c' || echo '$(srcdir)/'`fuse.c + +libfuse_lite_la-fuse_kern_chan.lo: fuse_kern_chan.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -MT libfuse_lite_la-fuse_kern_chan.lo -MD -MP -MF $(DEPDIR)/libfuse_lite_la-fuse_kern_chan.Tpo -c -o libfuse_lite_la-fuse_kern_chan.lo `test -f 'fuse_kern_chan.c' || echo '$(srcdir)/'`fuse_kern_chan.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfuse_lite_la-fuse_kern_chan.Tpo $(DEPDIR)/libfuse_lite_la-fuse_kern_chan.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuse_kern_chan.c' object='libfuse_lite_la-fuse_kern_chan.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -c -o libfuse_lite_la-fuse_kern_chan.lo `test -f 'fuse_kern_chan.c' || echo '$(srcdir)/'`fuse_kern_chan.c + +libfuse_lite_la-fuse_loop.lo: fuse_loop.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -MT libfuse_lite_la-fuse_loop.lo -MD -MP -MF $(DEPDIR)/libfuse_lite_la-fuse_loop.Tpo -c -o libfuse_lite_la-fuse_loop.lo `test -f 'fuse_loop.c' || echo '$(srcdir)/'`fuse_loop.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfuse_lite_la-fuse_loop.Tpo $(DEPDIR)/libfuse_lite_la-fuse_loop.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuse_loop.c' object='libfuse_lite_la-fuse_loop.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -c -o libfuse_lite_la-fuse_loop.lo `test -f 'fuse_loop.c' || echo '$(srcdir)/'`fuse_loop.c + +libfuse_lite_la-fuse_lowlevel.lo: fuse_lowlevel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -MT libfuse_lite_la-fuse_lowlevel.lo -MD -MP -MF $(DEPDIR)/libfuse_lite_la-fuse_lowlevel.Tpo -c -o libfuse_lite_la-fuse_lowlevel.lo `test -f 'fuse_lowlevel.c' || echo '$(srcdir)/'`fuse_lowlevel.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfuse_lite_la-fuse_lowlevel.Tpo $(DEPDIR)/libfuse_lite_la-fuse_lowlevel.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuse_lowlevel.c' object='libfuse_lite_la-fuse_lowlevel.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -c -o libfuse_lite_la-fuse_lowlevel.lo `test -f 'fuse_lowlevel.c' || echo '$(srcdir)/'`fuse_lowlevel.c + +libfuse_lite_la-fuse_opt.lo: fuse_opt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -MT libfuse_lite_la-fuse_opt.lo -MD -MP -MF $(DEPDIR)/libfuse_lite_la-fuse_opt.Tpo -c -o libfuse_lite_la-fuse_opt.lo `test -f 'fuse_opt.c' || echo '$(srcdir)/'`fuse_opt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfuse_lite_la-fuse_opt.Tpo $(DEPDIR)/libfuse_lite_la-fuse_opt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuse_opt.c' object='libfuse_lite_la-fuse_opt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -c -o libfuse_lite_la-fuse_opt.lo `test -f 'fuse_opt.c' || echo '$(srcdir)/'`fuse_opt.c + +libfuse_lite_la-fuse_session.lo: fuse_session.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -MT libfuse_lite_la-fuse_session.lo -MD -MP -MF $(DEPDIR)/libfuse_lite_la-fuse_session.Tpo -c -o libfuse_lite_la-fuse_session.lo `test -f 'fuse_session.c' || echo '$(srcdir)/'`fuse_session.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfuse_lite_la-fuse_session.Tpo $(DEPDIR)/libfuse_lite_la-fuse_session.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuse_session.c' object='libfuse_lite_la-fuse_session.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -c -o libfuse_lite_la-fuse_session.lo `test -f 'fuse_session.c' || echo '$(srcdir)/'`fuse_session.c + +libfuse_lite_la-fuse_signals.lo: fuse_signals.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -MT libfuse_lite_la-fuse_signals.lo -MD -MP -MF $(DEPDIR)/libfuse_lite_la-fuse_signals.Tpo -c -o libfuse_lite_la-fuse_signals.lo `test -f 'fuse_signals.c' || echo '$(srcdir)/'`fuse_signals.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfuse_lite_la-fuse_signals.Tpo $(DEPDIR)/libfuse_lite_la-fuse_signals.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuse_signals.c' object='libfuse_lite_la-fuse_signals.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -c -o libfuse_lite_la-fuse_signals.lo `test -f 'fuse_signals.c' || echo '$(srcdir)/'`fuse_signals.c + +libfuse_lite_la-fusermount.lo: fusermount.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -MT libfuse_lite_la-fusermount.lo -MD -MP -MF $(DEPDIR)/libfuse_lite_la-fusermount.Tpo -c -o libfuse_lite_la-fusermount.lo `test -f 'fusermount.c' || echo '$(srcdir)/'`fusermount.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfuse_lite_la-fusermount.Tpo $(DEPDIR)/libfuse_lite_la-fusermount.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fusermount.c' object='libfuse_lite_la-fusermount.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -c -o libfuse_lite_la-fusermount.lo `test -f 'fusermount.c' || echo '$(srcdir)/'`fusermount.c + +libfuse_lite_la-helper.lo: helper.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -MT libfuse_lite_la-helper.lo -MD -MP -MF $(DEPDIR)/libfuse_lite_la-helper.Tpo -c -o libfuse_lite_la-helper.lo `test -f 'helper.c' || echo '$(srcdir)/'`helper.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfuse_lite_la-helper.Tpo $(DEPDIR)/libfuse_lite_la-helper.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='helper.c' object='libfuse_lite_la-helper.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -c -o libfuse_lite_la-helper.lo `test -f 'helper.c' || echo '$(srcdir)/'`helper.c + +libfuse_lite_la-mount.lo: mount.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -MT libfuse_lite_la-mount.lo -MD -MP -MF $(DEPDIR)/libfuse_lite_la-mount.Tpo -c -o libfuse_lite_la-mount.lo `test -f 'mount.c' || echo '$(srcdir)/'`mount.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfuse_lite_la-mount.Tpo $(DEPDIR)/libfuse_lite_la-mount.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mount.c' object='libfuse_lite_la-mount.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -c -o libfuse_lite_la-mount.lo `test -f 'mount.c' || echo '$(srcdir)/'`mount.c + +libfuse_lite_la-mount_util.lo: mount_util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -MT libfuse_lite_la-mount_util.lo -MD -MP -MF $(DEPDIR)/libfuse_lite_la-mount_util.Tpo -c -o libfuse_lite_la-mount_util.lo `test -f 'mount_util.c' || echo '$(srcdir)/'`mount_util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfuse_lite_la-mount_util.Tpo $(DEPDIR)/libfuse_lite_la-mount_util.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mount_util.c' object='libfuse_lite_la-mount_util.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfuse_lite_la_CFLAGS) $(CFLAGS) -c -o libfuse_lite_la-mount_util.lo `test -f 'mount_util.c' || echo '$(srcdir)/'`mount_util.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + + +libs: libfuse-lite.la + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c new file mode 100755 index 0000000000000000000000000000000000000000..4c6c7133002dbea3eccdd21870ef2e21e707398b --- /dev/null +++ b/libfuse-lite/fuse.c @@ -0,0 +1,3243 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#ifdef __SOLARIS__ +/* For pthread_rwlock_t */ +#define _GNU_SOURCE +#endif /* __SOLARIS__ */ + +#include "config.h" +#include "fuse_i.h" +#include "fuse_lowlevel.h" +#include "fuse_opt.h" +#include "fuse_misc.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <unistd.h> +#include <time.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <signal.h> +#include <dlfcn.h> +#include <assert.h> +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/time.h> + +#ifdef __SOLARIS__ +#define FUSE_MAX_PATH 4096 +#endif /* __SOLARIS__ */ + +#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1 + +#define FUSE_UNKNOWN_INO 0xffffffff +#define OFFSET_MAX 0x7fffffffffffffffLL + +struct fuse_config { + unsigned int uid; + unsigned int gid; + unsigned int umask; + double entry_timeout; + double negative_timeout; + double attr_timeout; + double ac_attr_timeout; + int ac_attr_timeout_set; + int debug; + int hard_remove; + int use_ino; + int readdir_ino; + int set_mode; + int set_uid; + int set_gid; + int direct_io; + int kernel_cache; + int intr; + int intr_signal; + int help; +#ifdef __SOLARIS__ + int auto_cache; + char *modules; +#endif /* __SOLARIS__ */ +}; + +struct fuse_fs { + struct fuse_operations op; + void *user_data; +#ifdef __SOLARIS__ + struct fuse_module *m; +#endif /* __SOLARIS__ */ +}; + +#ifdef __SOLARIS__ +struct fusemod_so { + void *handle; + int ctr; +}; +#endif /* __SOLARIS__ */ + +struct fuse { + struct fuse_session *se; + struct node **name_table; + size_t name_table_size; + struct node **id_table; + size_t id_table_size; + fuse_ino_t ctr; + unsigned int generation; + unsigned int hidectr; + pthread_mutex_t lock; + pthread_rwlock_t tree_lock; + struct fuse_config conf; + int intr_installed; + struct fuse_fs *fs; +}; + +struct lock { + int type; + off_t start; + off_t end; + pid_t pid; + uint64_t owner; + struct lock *next; +}; + +struct node { + struct node *name_next; + struct node *id_next; + fuse_ino_t nodeid; + unsigned int generation; + int refctr; + struct node *parent; + char *name; + uint64_t nlookup; + int open_count; + int is_hidden; +#ifdef __SOLARIS__ + struct timespec stat_updated; + struct timespec mtime; + off_t size; + int cache_valid; +#endif /* __SOLARIS__ */ + struct lock *locks; +}; + +struct fuse_dh { + pthread_mutex_t lock; + struct fuse *fuse; + fuse_req_t req; + char *contents; + int allocated; + unsigned len; + unsigned size; + unsigned needlen; + int filled; + uint64_t fh; + int error; + fuse_ino_t nodeid; +}; + +struct fuse_context_i { + struct fuse_context ctx; + fuse_req_t req; +}; + +static pthread_key_t fuse_context_key; +static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER; +static int fuse_context_ref; + +#ifdef __SOLARIS__ + +static struct fusemod_so *fuse_current_so; +static struct fuse_module *fuse_modules; + +static int fuse_load_so_name(const char *soname) +{ + struct fusemod_so *so; + + so = calloc(1, sizeof(struct fusemod_so)); + if (!so) { + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; + } + + fuse_current_so = so; + so->handle = dlopen(soname, RTLD_NOW); + fuse_current_so = NULL; + if (!so->handle) { + fprintf(stderr, "fuse: %s\n", dlerror()); + goto err; + } + if (!so->ctr) { + fprintf(stderr, "fuse: %s did not register any modules", soname); + goto err; + } + return 0; + + err: + if (so->handle) + dlclose(so->handle); + free(so); + return -1; +} + +static int fuse_load_so_module(const char *module) +{ + int res; + char *soname = malloc(strlen(module) + 64); + if (!soname) { + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; + } + sprintf(soname, "libfusemod_%s.so", module); + res = fuse_load_so_name(soname); + free(soname); + return res; +} + +static struct fuse_module *fuse_find_module(const char *module) +{ + struct fuse_module *m; + for (m = fuse_modules; m; m = m->next) { + if (strcmp(module, m->name) == 0) { + m->ctr++; + break; + } + } + return m; +} + +static struct fuse_module *fuse_get_module(const char *module) +{ + struct fuse_module *m; + + pthread_mutex_lock(&fuse_context_lock); + m = fuse_find_module(module); + if (!m) { + int err = fuse_load_so_module(module); + if (!err) + m = fuse_find_module(module); + } + pthread_mutex_unlock(&fuse_context_lock); + return m; +} + +static void fuse_put_module(struct fuse_module *m) +{ + pthread_mutex_lock(&fuse_context_lock); + assert(m->ctr > 0); + m->ctr--; + if (!m->ctr && m->so) { + struct fusemod_so *so = m->so; + assert(so->ctr > 0); + so->ctr--; + if (!so->ctr) { + struct fuse_module **mp; + for (mp = &fuse_modules; *mp;) { + if ((*mp)->so == so) + *mp = (*mp)->next; + else + mp = &(*mp)->next; + } + dlclose(so->handle); + free(so); + } + } + pthread_mutex_unlock(&fuse_context_lock); +} +#endif /* __SOLARIS__ */ + +static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid) +{ + size_t hash = nodeid % f->id_table_size; + struct node *node; + + for (node = f->id_table[hash]; node != NULL; node = node->id_next) + if (node->nodeid == nodeid) + return node; + + return NULL; +} + +static struct node *get_node(struct fuse *f, fuse_ino_t nodeid) +{ + struct node *node = get_node_nocheck(f, nodeid); + if (!node) { + fprintf(stderr, "fuse internal error: node %llu not found\n", + (unsigned long long) nodeid); + abort(); + } + return node; +} + +static void free_node(struct node *node) +{ + free(node->name); + free(node); +} + +static void unhash_id(struct fuse *f, struct node *node) +{ + size_t hash = node->nodeid % f->id_table_size; + struct node **nodep = &f->id_table[hash]; + + for (; *nodep != NULL; nodep = &(*nodep)->id_next) + if (*nodep == node) { + *nodep = node->id_next; + return; + } +} + +static void hash_id(struct fuse *f, struct node *node) +{ + size_t hash = node->nodeid % f->id_table_size; + node->id_next = f->id_table[hash]; + f->id_table[hash] = node; +} + +static unsigned int name_hash(struct fuse *f, fuse_ino_t parent, + const char *name) +{ + unsigned int hash = *name; + + if (hash) + for (name += 1; *name != '\0'; name++) + hash = (hash << 5) - hash + *name; + + return (hash + parent) % f->name_table_size; +} + +static void unref_node(struct fuse *f, struct node *node); + +static void unhash_name(struct fuse *f, struct node *node) +{ + if (node->name) { + size_t hash = name_hash(f, node->parent->nodeid, node->name); + struct node **nodep = &f->name_table[hash]; + + for (; *nodep != NULL; nodep = &(*nodep)->name_next) + if (*nodep == node) { + *nodep = node->name_next; + node->name_next = NULL; + unref_node(f, node->parent); + free(node->name); + node->name = NULL; + node->parent = NULL; + return; + } + fprintf(stderr, "fuse internal error: unable to unhash node: %llu\n", + (unsigned long long) node->nodeid); + abort(); + } +} + +static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid, + const char *name) +{ + size_t hash = name_hash(f, parentid, name); + struct node *parent = get_node(f, parentid); + node->name = strdup(name); + if (node->name == NULL) + return -1; + + parent->refctr ++; + node->parent = parent; + node->name_next = f->name_table[hash]; + f->name_table[hash] = node; + return 0; +} + +static void delete_node(struct fuse *f, struct node *node) +{ + if (f->conf.debug) + fprintf(stderr, "delete: %llu\n", (unsigned long long) node->nodeid); + + assert(!node->name); + unhash_id(f, node); + free_node(node); +} + +static void unref_node(struct fuse *f, struct node *node) +{ + assert(node->refctr > 0); + node->refctr --; + if (!node->refctr) + delete_node(f, node); +} + +static fuse_ino_t next_id(struct fuse *f) +{ + do { + f->ctr = (f->ctr + 1) & 0xffffffff; + if (!f->ctr) + f->generation ++; + } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO || + get_node_nocheck(f, f->ctr) != NULL); + return f->ctr; +} + +static struct node *lookup_node(struct fuse *f, fuse_ino_t parent, + const char *name) +{ + size_t hash = name_hash(f, parent, name); + struct node *node; + + for (node = f->name_table[hash]; node != NULL; node = node->name_next) + if (node->parent->nodeid == parent && strcmp(node->name, name) == 0) + return node; + + return NULL; +} + +static struct node *find_node(struct fuse *f, fuse_ino_t parent, + const char *name) +{ + struct node *node; + + pthread_mutex_lock(&f->lock); + node = lookup_node(f, parent, name); + if (node == NULL) { + node = (struct node *) calloc(1, sizeof(struct node)); + if (node == NULL) + goto out_err; + + node->refctr = 1; + node->nodeid = next_id(f); + node->open_count = 0; + node->is_hidden = 0; + node->generation = f->generation; + if (hash_name(f, node, parent, name) == -1) { + free(node); + node = NULL; + goto out_err; + } + hash_id(f, node); + } + node->nlookup ++; + out_err: + pthread_mutex_unlock(&f->lock); + return node; +} + +#ifndef __SOLARIS__ +static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name) +#else /* __SOLARIS__ */ +static char *add_name(char *buf, char *s, const char *name) +#endif /* __SOLARIS__ */ +{ + size_t len = strlen(name); + +#ifndef __SOLARIS__ + if (s - len <= *buf) { + unsigned pathlen = *bufsize - (s - *buf); + unsigned newbufsize = *bufsize; + char *newbuf; + + while (newbufsize < pathlen + len + 1) { + if (newbufsize >= 0x80000000) + newbufsize = 0xffffffff; + else + newbufsize *= 2; + } + + newbuf = realloc(*buf, newbufsize); + if (newbuf == NULL) + return NULL; + + *buf = newbuf; + s = newbuf + newbufsize - pathlen; + memmove(s, newbuf + *bufsize - pathlen, pathlen); + *bufsize = newbufsize; + } + s -= len; +#else /* ! __SOLARIS__ */ + s -= len; + if (s <= buf) { + fprintf(stderr, "fuse: path too long: ...%s\n", s + len); + return NULL; + } +#endif /* __SOLARIS__ */ + strncpy(s, name, len); + s--; + *s = '/'; + + return s; +} + +static char *get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name) +{ +#ifdef __SOLARIS__ + char buf[FUSE_MAX_PATH]; + char *s = buf + FUSE_MAX_PATH - 1; + struct node *node; + + *s = '\0'; + + if (name != NULL) { + s = add_name(buf, s, name); + if (s == NULL) + return NULL; + } + + pthread_mutex_lock(&f->lock); + for (node = get_node(f, nodeid); node && node->nodeid != FUSE_ROOT_ID; + node = node->parent) { + if (node->name == NULL) { + s = NULL; + break; + } + + s = add_name(buf, s, node->name); + if (s == NULL) + break; + } + pthread_mutex_unlock(&f->lock); + + if (node == NULL || s == NULL) + return NULL; + else if (*s == '\0') + return strdup("/"); + else + return strdup(s); + +#else /* __SOLARIS__ */ + + unsigned bufsize = 256; + char *buf; + char *s; + struct node *node; + + buf = malloc(bufsize); + if (buf == NULL) + return NULL; + + s = buf + bufsize - 1; + *s = '\0'; + + if (name != NULL) { + s = add_name(&buf, &bufsize, s, name); + if (s == NULL) + goto out_free; + } + + pthread_mutex_lock(&f->lock); + for (node = get_node(f, nodeid); node && node->nodeid != FUSE_ROOT_ID; + node = node->parent) { + if (node->name == NULL) { + s = NULL; + break; + } + + s = add_name(&buf, &bufsize, s, node->name); + if (s == NULL) + break; + } + pthread_mutex_unlock(&f->lock); + + if (node == NULL || s == NULL) + goto out_free; + + if (s[0]) + memmove(buf, s, bufsize - (s - buf)); + else + strcpy(buf, "/"); + return buf; + +out_free: + free(buf); + return NULL; +#endif /* __SOLARIS__ */ +} + +static char *get_path(struct fuse *f, fuse_ino_t nodeid) +{ + return get_path_name(f, nodeid, NULL); +} + +static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup) +{ + struct node *node; + if (nodeid == FUSE_ROOT_ID) + return; + pthread_mutex_lock(&f->lock); + node = get_node(f, nodeid); + assert(node->nlookup >= nlookup); + node->nlookup -= nlookup; + if (!node->nlookup) { + unhash_name(f, node); + unref_node(f, node); + } + pthread_mutex_unlock(&f->lock); +} + +static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name) +{ + struct node *node; + + pthread_mutex_lock(&f->lock); + node = lookup_node(f, dir, name); + if (node != NULL) + unhash_name(f, node); + pthread_mutex_unlock(&f->lock); +} + +static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname, + fuse_ino_t newdir, const char *newname, int hide) +{ + struct node *node; + struct node *newnode; + int err = 0; + + pthread_mutex_lock(&f->lock); + node = lookup_node(f, olddir, oldname); + newnode = lookup_node(f, newdir, newname); + if (node == NULL) + goto out; + + if (newnode != NULL) { + if (hide) { + fprintf(stderr, "fuse: hidden file got created during hiding\n"); + err = -EBUSY; + goto out; + } + unhash_name(f, newnode); + } + + unhash_name(f, node); + if (hash_name(f, node, newdir, newname) == -1) { + err = -ENOMEM; + goto out; + } + + if (hide) + node->is_hidden = 1; + + out: + pthread_mutex_unlock(&f->lock); + return err; +} + +static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf) +{ + if (!f->conf.use_ino) + stbuf->st_ino = nodeid; + if (f->conf.set_mode) + stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f->conf.umask); + if (f->conf.set_uid) + stbuf->st_uid = f->conf.uid; + if (f->conf.set_gid) + stbuf->st_gid = f->conf.gid; +} + +static struct fuse *req_fuse(fuse_req_t req) +{ + return (struct fuse *) fuse_req_userdata(req); +} + +static void fuse_intr_sighandler(int sig) +{ + (void) sig; + /* Nothing to do */ +} + +struct fuse_intr_data { + pthread_t id; + pthread_cond_t cond; + int finished; +}; + +static void fuse_interrupt(fuse_req_t req, void *d_) +{ + struct fuse_intr_data *d = d_; + struct fuse *f = req_fuse(req); + + if (d->id == pthread_self()) + return; + + pthread_mutex_lock(&f->lock); + while (!d->finished) { + struct timeval now; + struct timespec timeout; + + pthread_kill(d->id, f->conf.intr_signal); + gettimeofday(&now, NULL); + timeout.tv_sec = now.tv_sec + 1; + timeout.tv_nsec = now.tv_usec * 1000; + pthread_cond_timedwait(&d->cond, &f->lock, &timeout); + } + pthread_mutex_unlock(&f->lock); +} + +static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req, + struct fuse_intr_data *d) +{ + pthread_mutex_lock(&f->lock); + d->finished = 1; + pthread_cond_broadcast(&d->cond); + pthread_mutex_unlock(&f->lock); + fuse_req_interrupt_func(req, NULL, NULL); + pthread_cond_destroy(&d->cond); +} + +static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d) +{ + d->id = pthread_self(); + pthread_cond_init(&d->cond, NULL); + d->finished = 0; + fuse_req_interrupt_func(req, fuse_interrupt, d); +} + +static void fuse_finish_interrupt(struct fuse *f, fuse_req_t req, + struct fuse_intr_data *d) +{ + if (f->conf.intr) + fuse_do_finish_interrupt(f, req, d); +} + +static void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req, + struct fuse_intr_data *d) +{ + if (f->conf.intr) + fuse_do_prepare_interrupt(req, d); +} + +int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.getattr) + return fs->op.getattr(path, buf); + else + return -ENOSYS; +} + +int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.fgetattr) + return fs->op.fgetattr(path, buf, fi); + else if (fs->op.getattr) + return fs->op.getattr(path, buf); + else + return -ENOSYS; +} + +int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, + const char *newpath) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.rename) + return fs->op.rename(oldpath, newpath); + else + return -ENOSYS; +} + +int fuse_fs_unlink(struct fuse_fs *fs, const char *path) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.unlink) + return fs->op.unlink(path); + else + return -ENOSYS; +} + +int fuse_fs_rmdir(struct fuse_fs *fs, const char *path) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.rmdir) + return fs->op.rmdir(path); + else + return -ENOSYS; +} + +int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.symlink) + return fs->op.symlink(linkname, path); + else + return -ENOSYS; +} + +int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.link) + return fs->op.link(oldpath, newpath); + else + return -ENOSYS; +} + +int fuse_fs_release(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.release) + return fs->op.release(path, fi); + else + return 0; +} + +int fuse_fs_opendir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.opendir) + return fs->op.opendir(path, fi); + else + return 0; +} + +int fuse_fs_open(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.open) + return fs->op.open(path, fi); + else + return 0; +} + +int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, + off_t off, struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.read) + return fs->op.read(path, buf, size, off, fi); + else + return -ENOSYS; +} + +int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.write) + return fs->op.write(path, buf, size, off, fi); + else + return -ENOSYS; +} + +int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.fsync) + return fs->op.fsync(path, datasync, fi); + else + return -ENOSYS; +} + +int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.fsyncdir) + return fs->op.fsyncdir(path, datasync, fi); + else + return -ENOSYS; +} + +int fuse_fs_flush(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.flush) + return fs->op.flush(path, fi); + else + return -ENOSYS; +} + +int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.statfs) + return fs->op.statfs(path, buf); + else { + buf->f_namemax = 255; + buf->f_bsize = 512; + return 0; + } +} + +int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.releasedir) + return fs->op.releasedir(path, fi); + else + return 0; +} + +int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, + fuse_fill_dir_t filler, off_t off, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.readdir) + return fs->op.readdir(path, buf, filler, off, fi); + else + return -ENOSYS; +} + +int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.create) + return fs->op.create(path, mode, fi); + else + return -ENOSYS; +} + +int fuse_fs_lock(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi, int cmd, struct flock *lock) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.lock) + return fs->op.lock(path, fi, cmd, lock); + else + return -ENOSYS; +} + +int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.chown) + return fs->op.chown(path, uid, gid); + else + return -ENOSYS; +} + +int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.truncate) + return fs->op.truncate(path, size); + else + return -ENOSYS; +} + +int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off_t size, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.ftruncate) + return fs->op.ftruncate(path, size, fi); + else if (fs->op.truncate) + return fs->op.truncate(path, size); + else + return -ENOSYS; +} + +int fuse_fs_utimens(struct fuse_fs *fs, const char *path, + const struct timespec tv[2]) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.utimens) + return fs->op.utimens(path, tv); + else if(fs->op.utime) { + struct utimbuf buf; + buf.actime = tv[0].tv_sec; + buf.modtime = tv[1].tv_sec; + return fs->op.utime(path, &buf); + } else + return -ENOSYS; +} + +int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.access) + return fs->op.access(path, mask); + else + return -ENOSYS; +} + +int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, + size_t len) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.readlink) + return fs->op.readlink(path, buf, len); + else + return -ENOSYS; +} + +int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, + dev_t rdev) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.mknod) + return fs->op.mknod(path, mode, rdev); + else + return -ENOSYS; +} + +int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.mkdir) + return fs->op.mkdir(path, mode); + else + return -ENOSYS; +} + +int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, + const char *value, size_t size, int flags) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.setxattr) + return fs->op.setxattr(path, name, value, size, flags); + else + return -ENOSYS; +} + +int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, + char *value, size_t size) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.getxattr) + return fs->op.getxattr(path, name, value, size); + else + return -ENOSYS; +} + +int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, + size_t size) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.listxattr) + return fs->op.listxattr(path, list, size); + else + return -ENOSYS; +} + +int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, + uint64_t *idx) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.bmap) + return fs->op.bmap(path, blocksize, idx); + else + return -ENOSYS; +} + +int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.removexattr) + return fs->op.removexattr(path, name); + else + return -ENOSYS; +} + +int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg, + struct fuse_file_info *fi, unsigned int flags, void *data) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.ioctl) { +/* + if (fs->debug) + fprintf(stderr, "ioctl[%llu] 0x%x flags: 0x%x\n", + (unsigned long long) fi->fh, cmd, flags); +*/ + return fs->op.ioctl(path, cmd, arg, fi, flags, data); + } else + return -ENOSYS; +} + +static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) +{ + struct node *node; + int isopen = 0; + pthread_mutex_lock(&f->lock); + node = lookup_node(f, dir, name); + if (node && node->open_count > 0) + isopen = 1; + pthread_mutex_unlock(&f->lock); + return isopen; +} + +static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname, + char *newname, size_t bufsize) +{ + struct stat buf; + struct node *node; + struct node *newnode; + char *newpath; + int res; + int failctr = 10; + + do { + pthread_mutex_lock(&f->lock); + node = lookup_node(f, dir, oldname); + if (node == NULL) { + pthread_mutex_unlock(&f->lock); + return NULL; + } + do { + f->hidectr ++; + snprintf(newname, bufsize, ".fuse_hidden%08x%08x", + (unsigned int) node->nodeid, f->hidectr); + newnode = lookup_node(f, dir, newname); + } while(newnode); + pthread_mutex_unlock(&f->lock); + + newpath = get_path_name(f, dir, newname); + if (!newpath) + break; + + res = fuse_fs_getattr(f->fs, newpath, &buf); + if (res == -ENOENT) + break; + free(newpath); + newpath = NULL; + } while(res == 0 && --failctr); + + return newpath; +} + +static int hide_node(struct fuse *f, const char *oldpath, + fuse_ino_t dir, const char *oldname) +{ + char newname[64]; + char *newpath; + int err = -EBUSY; + + newpath = hidden_name(f, dir, oldname, newname, sizeof(newname)); + if (newpath) { + err = fuse_fs_rename(f->fs, oldpath, newpath); + if (!err) + err = rename_node(f, dir, oldname, dir, newname, 1); + free(newpath); + } + return err; +} + +#ifdef __SOLARIS__ + +static int mtime_eq(const struct stat *stbuf, const struct timespec *ts) +{ + return stbuf->st_mtime == ts->tv_sec && ST_MTIM_NSEC(stbuf) == ts->tv_nsec; +} + +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC CLOCK_REALTIME +#endif + +static void curr_time(struct timespec *now) +{ + static clockid_t clockid = CLOCK_MONOTONIC; + int res = clock_gettime(clockid, now); + if (res == -1 && errno == EINVAL) { + clockid = CLOCK_REALTIME; + res = clock_gettime(clockid, now); + } + if (res == -1) { + perror("fuse: clock_gettime"); + abort(); + } +} + +static void update_stat(struct node *node, const struct stat *stbuf) +{ + if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) || + stbuf->st_size != node->size)) + node->cache_valid = 0; + node->mtime.tv_sec = stbuf->st_mtime; + node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf); + node->size = stbuf->st_size; + curr_time(&node->stat_updated); +} + +#endif /* __SOLARIS__ */ + +static int lookup_path(struct fuse *f, fuse_ino_t nodeid, + const char *name, const char *path, + struct fuse_entry_param *e, struct fuse_file_info *fi) +{ + int res; + + memset(e, 0, sizeof(struct fuse_entry_param)); + if (fi) + res = fuse_fs_fgetattr(f->fs, path, &e->attr, fi); + else + res = fuse_fs_getattr(f->fs, path, &e->attr); + if (res == 0) { + struct node *node; + + node = find_node(f, nodeid, name); + if (node == NULL) + res = -ENOMEM; + else { + e->ino = node->nodeid; + e->generation = node->generation; + e->entry_timeout = f->conf.entry_timeout; + e->attr_timeout = f->conf.attr_timeout; +#ifdef __SOLARIS__ + if (f->conf.auto_cache) { + pthread_mutex_lock(&f->lock); + update_stat(node, &e->attr); + pthread_mutex_unlock(&f->lock); + } +#endif /* __SOLARIS__ */ + set_stat(f, e->ino, &e->attr); + if (f->conf.debug) + fprintf(stderr, " NODEID: %lu\n", (unsigned long) e->ino); + } + } + return res; +} + +static struct fuse_context_i *fuse_get_context_internal(void) +{ + struct fuse_context_i *c; + + c = (struct fuse_context_i *) pthread_getspecific(fuse_context_key); + if (c == NULL) { + c = (struct fuse_context_i *) malloc(sizeof(struct fuse_context_i)); + if (c == NULL) { + /* This is hard to deal with properly, so just abort. If + memory is so low that the context cannot be allocated, + there's not much hope for the filesystem anyway */ + fprintf(stderr, "fuse: failed to allocate thread specific data\n"); + abort(); + } + pthread_setspecific(fuse_context_key, c); + } + return c; +} + +static void fuse_freecontext(void *data) +{ + free(data); +} + +static int fuse_create_context_key(void) +{ + int err = 0; + pthread_mutex_lock(&fuse_context_lock); + if (!fuse_context_ref) { + err = pthread_key_create(&fuse_context_key, fuse_freecontext); + if (err) { + fprintf(stderr, "fuse: failed to create thread specific key: %s\n", + strerror(err)); + pthread_mutex_unlock(&fuse_context_lock); + return -1; + } + } + fuse_context_ref++; + pthread_mutex_unlock(&fuse_context_lock); + return 0; +} + +static void fuse_delete_context_key(void) +{ + pthread_mutex_lock(&fuse_context_lock); + fuse_context_ref--; + if (!fuse_context_ref) { + free(pthread_getspecific(fuse_context_key)); + pthread_key_delete(fuse_context_key); + } + pthread_mutex_unlock(&fuse_context_lock); +} + +static struct fuse *req_fuse_prepare(fuse_req_t req) +{ + struct fuse_context_i *c = fuse_get_context_internal(); + const struct fuse_ctx *ctx = fuse_req_ctx(req); + c->req = req; + c->ctx.fuse = req_fuse(req); + c->ctx.uid = ctx->uid; + c->ctx.gid = ctx->gid; + c->ctx.pid = ctx->pid; +#ifdef POSIXACLS + c->ctx.umask = ctx->umask; +#endif + return c->ctx.fuse; +} + +#ifndef __SOLARIS__ +static void reply_err(fuse_req_t req, int err) +#else /* __SOLARIS__ */ +static inline void reply_err(fuse_req_t req, int err) +#endif /* __SOLARIS__ */ +{ + /* fuse_reply_err() uses non-negated errno values */ + fuse_reply_err(req, -err); +} + +static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e, + int err) +{ + if (!err) { + struct fuse *f = req_fuse(req); + if (fuse_reply_entry(req, e) == -ENOENT) + forget_node(f, e->ino, 1); + } else + reply_err(req, err); +} + +void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.init) + fs->user_data = fs->op.init(conn); +} + +static void fuse_lib_init(void *data, struct fuse_conn_info *conn) +{ + struct fuse *f = (struct fuse *) data; + struct fuse_context_i *c = fuse_get_context_internal(); + + memset(c, 0, sizeof(*c)); + c->ctx.fuse = f; + fuse_fs_init(f->fs, conn); +} + +void fuse_fs_destroy(struct fuse_fs *fs) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.destroy) + fs->op.destroy(fs->user_data); +#ifdef __SOLARIS__ + if (fs->m) + fuse_put_module(fs->m); +#endif /* __SOLARIS__ */ + free(fs); +} + +static void fuse_lib_destroy(void *data) +{ + struct fuse *f = (struct fuse *) data; + struct fuse_context_i *c = fuse_get_context_internal(); + + memset(c, 0, sizeof(*c)); + c->ctx.fuse = f; + fuse_fs_destroy(f->fs); + f->fs = NULL; +} + +static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent, + const char *name) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_entry_param e; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "LOOKUP %s\n", path); + fuse_prepare_interrupt(f, req, &d); + err = lookup_path(f, parent, name, path, &e, NULL); + if (err == -ENOENT && f->conf.negative_timeout != 0.0) { + e.ino = 0; + e.entry_timeout = f->conf.negative_timeout; + err = 0; + } + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_entry(req, &e, err); +} + +static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino, + unsigned long nlookup) +{ + struct fuse *f = req_fuse(req); + if (f->conf.debug) + fprintf(stderr, "FORGET %llu/%lu\n", (unsigned long long)ino, nlookup); + forget_node(f, ino, nlookup); + fuse_reply_none(req); +} + +static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct stat buf; + char *path; + int err; + + (void) fi; + memset(&buf, 0, sizeof(buf)); + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_getattr(f->fs, path, &buf); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + if (!err) { +#ifdef __SOLARIS__ + if (f->conf.auto_cache) { + pthread_mutex_lock(&f->lock); + update_stat(get_node(f, ino), &buf); + pthread_mutex_unlock(&f->lock); + } +#endif /* __SOLARIS__ */ + set_stat(f, ino, &buf); + fuse_reply_attr(req, &buf, f->conf.attr_timeout); + } else + reply_err(req, err); +} + +int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.chmod) + return fs->op.chmod(path, mode); + else + return -ENOSYS; +} + +static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int valid, struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct stat buf; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = 0; + if (!err && (valid & FUSE_SET_ATTR_MODE)) + err = fuse_fs_chmod(f->fs, path, attr->st_mode); + if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) { + uid_t uid = + (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t) -1; + gid_t gid = + (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t) -1; + err = fuse_fs_chown(f->fs, path, uid, gid); + } + if (!err && (valid & FUSE_SET_ATTR_SIZE)) { + if (fi) + err = fuse_fs_ftruncate(f->fs, path, attr->st_size, fi); + else + err = fuse_fs_truncate(f->fs, path, attr->st_size); + } +#ifdef HAVE_UTIMENSAT + if (!err && + (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) { + struct timespec tv[2]; + + tv[0].tv_sec = 0; + tv[1].tv_sec = 0; + tv[0].tv_nsec = UTIME_OMIT; + tv[1].tv_nsec = UTIME_OMIT; + + if (valid & FUSE_SET_ATTR_ATIME_NOW) + tv[0].tv_nsec = UTIME_NOW; + else if (valid & FUSE_SET_ATTR_ATIME) + tv[0] = attr->st_atim; + + if (valid & FUSE_SET_ATTR_MTIME_NOW) + tv[1].tv_nsec = UTIME_NOW; + else if (valid & FUSE_SET_ATTR_MTIME) + tv[1] = attr->st_mtim; + + err = fuse_fs_utimens(f->fs, path, tv); + } else +#endif + if (!err && (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) == + (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { + struct timespec tv[2]; + tv[0].tv_sec = attr->st_atime; + tv[0].tv_nsec = ST_ATIM_NSEC(attr); + tv[1].tv_sec = attr->st_mtime; + tv[1].tv_nsec = ST_MTIM_NSEC(attr); + err = fuse_fs_utimens(f->fs, path, tv); + } + if (!err) + err = fuse_fs_getattr(f->fs, path, &buf); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + if (!err) { +#ifdef __SOLARIS__ + if (f->conf.auto_cache) { + pthread_mutex_lock(&f->lock); + update_stat(get_node(f, ino), &buf); + pthread_mutex_unlock(&f->lock); + } +#endif /* __SOLARIS__ */ + set_stat(f, ino, &buf); + fuse_reply_attr(req, &buf, f->conf.attr_timeout); + } else + reply_err(req, err); +} + +static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "ACCESS %s 0%o\n", path, mask); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_access(f->fs, path, mask); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino) +{ + struct fuse *f = req_fuse_prepare(req); + char linkname[PATH_MAX + 1]; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname)); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + if (!err) { + linkname[PATH_MAX] = '\0'; + fuse_reply_readlink(req, linkname); + } else + reply_err(req, err); +} + +static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, dev_t rdev) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_entry_param e; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "MKNOD %s\n", path); + fuse_prepare_interrupt(f, req, &d); + err = -ENOSYS; + if (S_ISREG(mode)) { + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = O_CREAT | O_EXCL | O_WRONLY; + err = fuse_fs_create(f->fs, path, mode, &fi); + if (!err) { + err = lookup_path(f, parent, name, path, &e, &fi); + fuse_fs_release(f->fs, path, &fi); + } + } + if (err == -ENOSYS) { + err = fuse_fs_mknod(f->fs, path, mode, rdev); + if (!err) + err = lookup_path(f, parent, name, path, &e, NULL); + } + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_entry(req, &e, err); +} + +static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_entry_param e; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "MKDIR %s\n", path); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_mkdir(f->fs, path, mode); + if (!err) + err = lookup_path(f, parent, name, path, &e, NULL); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_entry(req, &e, err); +} + +static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent, + const char *name) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_wrlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "UNLINK %s\n", path); + fuse_prepare_interrupt(f, req, &d); + if (!f->conf.hard_remove && is_open(f, parent, name)) + err = hide_node(f, path, parent, name); + else { + err = fuse_fs_unlink(f->fs, path); + if (!err) + remove_node(f, parent, name); + } + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_wrlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "RMDIR %s\n", path); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_rmdir(f->fs, path); + fuse_finish_interrupt(f, req, &d); + if (!err) + remove_node(f, parent, name); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static void fuse_lib_symlink(fuse_req_t req, const char *linkname, + fuse_ino_t parent, const char *name) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_entry_param e; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "SYMLINK %s\n", path); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_symlink(f->fs, linkname, path); + if (!err) + err = lookup_path(f, parent, name, path, &e, NULL); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_entry(req, &e, err); +} + +static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir, + const char *oldname, fuse_ino_t newdir, + const char *newname) +{ + struct fuse *f = req_fuse_prepare(req); + char *oldpath; + char *newpath; + int err; + + err = -ENOENT; + pthread_rwlock_wrlock(&f->tree_lock); + oldpath = get_path_name(f, olddir, oldname); + if (oldpath != NULL) { + newpath = get_path_name(f, newdir, newname); + if (newpath != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "RENAME %s -> %s\n", oldpath, newpath); + err = 0; + fuse_prepare_interrupt(f, req, &d); + if (!f->conf.hard_remove && is_open(f, newdir, newname)) + err = hide_node(f, newpath, newdir, newname); + if (!err) { + err = fuse_fs_rename(f->fs, oldpath, newpath); + if (!err) + err = rename_node(f, olddir, oldname, newdir, newname, 0); + } + fuse_finish_interrupt(f, req, &d); + free(newpath); + } + free(oldpath); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, + const char *newname) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_entry_param e; + char *oldpath; + char *newpath; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + oldpath = get_path(f, ino); + if (oldpath != NULL) { + newpath = get_path_name(f, newparent, newname); + if (newpath != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "LINK %s\n", newpath); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_link(f->fs, oldpath, newpath); + if (!err) + err = lookup_path(f, newparent, newname, newpath, &e, NULL); + fuse_finish_interrupt(f, req, &d); + free(newpath); + } + free(oldpath); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_entry(req, &e, err); +} + +static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path, + struct fuse_file_info *fi) +{ + struct node *node; + int unlink_hidden = 0; + + fuse_fs_release(f->fs, path ? path : "-", fi); + + pthread_mutex_lock(&f->lock); + node = get_node(f, ino); + assert(node->open_count > 0); + --node->open_count; + if (node->is_hidden && !node->open_count) { + unlink_hidden = 1; + node->is_hidden = 0; + } + pthread_mutex_unlock(&f->lock); + + if(unlink_hidden && path) + fuse_fs_unlink(f->fs, path); +} + +static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + struct fuse_entry_param e; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_create(f->fs, path, mode, fi); + if (!err) { + err = lookup_path(f, parent, name, path, &e, fi); + if (err) + fuse_fs_release(f->fs, path, fi); + else if (!S_ISREG(e.attr.st_mode)) { + err = -EIO; + fuse_fs_release(f->fs, path, fi); + forget_node(f, e.ino, 1); + } else { + if (f->conf.direct_io) + fi->direct_io = 1; + if (f->conf.kernel_cache) + fi->keep_cache = 1; + + } + } + fuse_finish_interrupt(f, req, &d); + } + if (!err) { + pthread_mutex_lock(&f->lock); + get_node(f, e.ino)->open_count++; + pthread_mutex_unlock(&f->lock); + if (fuse_reply_create(req, &e, fi) == -ENOENT) { + /* The open syscall was interrupted, so it must be cancelled */ + fuse_prepare_interrupt(f, req, &d); + fuse_do_release(f, e.ino, path, fi); + fuse_finish_interrupt(f, req, &d); + forget_node(f, e.ino, 1); + } else if (f->conf.debug) { + fprintf(stderr, " CREATE[%llu] flags: 0x%x %s\n", + (unsigned long long) fi->fh, fi->flags, path); + } + } else + reply_err(req, err); + + if (path) + free(path); + + pthread_rwlock_unlock(&f->tree_lock); +} + +#ifdef __SOLARIS__ + +static double diff_timespec(const struct timespec *t1, + const struct timespec *t2) +{ + return (t1->tv_sec - t2->tv_sec) + + ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0; +} + +static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path, + struct fuse_file_info *fi) +{ + struct node *node; + + pthread_mutex_lock(&f->lock); + node = get_node(f, ino); + if (node->cache_valid) { + struct timespec now; + + curr_time(&now); + if (diff_timespec(&now, &node->stat_updated) > f->conf.ac_attr_timeout) { + struct stat stbuf; + int err; + pthread_mutex_unlock(&f->lock); + err = fuse_fs_fgetattr(f->fs, path, &stbuf, fi); + pthread_mutex_lock(&f->lock); + if (!err) + update_stat(node, &stbuf); + else + node->cache_valid = 0; + } + } + if (node->cache_valid) + fi->keep_cache = 1; + + node->cache_valid = 1; + pthread_mutex_unlock(&f->lock); +} + +#endif /* __SOLARIS__ */ + +static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + char *path = NULL; + int err = 0; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_open(f->fs, path, fi); + if (!err) { + if (f->conf.direct_io) + fi->direct_io = 1; + if (f->conf.kernel_cache) + fi->keep_cache = 1; +#ifdef __SOLARIS__ + + if (f->conf.auto_cache) + open_auto_cache(f, ino, path, fi); +#endif /* __SOLARIS__ */ + } + fuse_finish_interrupt(f, req, &d); + } + if (!err) { + pthread_mutex_lock(&f->lock); + get_node(f, ino)->open_count++; + pthread_mutex_unlock(&f->lock); + if (fuse_reply_open(req, fi) == -ENOENT) { + /* The open syscall was interrupted, so it must be cancelled */ + fuse_prepare_interrupt(f, req, &d); + fuse_do_release(f, ino, path, fi); + fuse_finish_interrupt(f, req, &d); + } else if (f->conf.debug) { + fprintf(stderr, "OPEN[%llu] flags: 0x%x %s\n", + (unsigned long long) fi->fh, fi->flags, path); + } + } else + reply_err(req, err); + + if (path) + free(path); + pthread_rwlock_unlock(&f->tree_lock); +} + +static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + char *buf; + int res; + + buf = (char *) malloc(size); + if (buf == NULL) { + reply_err(req, -ENOMEM); + return; + } + + res = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "READ[%llu] %lu bytes from %llu\n", + (unsigned long long) fi->fh, (unsigned long) size, + (unsigned long long) off); + + fuse_prepare_interrupt(f, req, &d); + res = fuse_fs_read(f->fs, path, buf, size, off, fi); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + + if (res >= 0) { + if (f->conf.debug) + fprintf(stderr, " READ[%llu] %u bytes\n", + (unsigned long long)fi->fh, res); + if ((size_t) res > size) + fprintf(stderr, "fuse: read too many bytes"); + fuse_reply_buf(req, buf, res); + } else + reply_err(req, res); + + free(buf); +} + +static void fuse_lib_write(fuse_req_t req, fuse_ino_t ino, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int res; + + res = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "WRITE%s[%llu] %lu bytes to %llu\n", + fi->writepage ? "PAGE" : "", (unsigned long long) fi->fh, + (unsigned long) size, (unsigned long long) off); + + fuse_prepare_interrupt(f, req, &d); + res = fuse_fs_write(f->fs, path, buf, size, off, fi); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + + if (res >= 0) { + if (f->conf.debug) + fprintf(stderr, " WRITE%s[%llu] %u bytes\n", + fi->writepage ? "PAGE" : "", (unsigned long long) fi->fh, + res); + if ((size_t) res > size) + fprintf(stderr, "fuse: wrote too many bytes"); + fuse_reply_write(req, res); + } else + reply_err(req, res); +} + +static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + if (f->conf.debug) + fprintf(stderr, "FSYNC[%llu]\n", (unsigned long long) fi->fh); + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_fsync(f->fs, path, datasync, fi); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi, + struct fuse_file_info *fi) +{ + struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh; + memset(fi, 0, sizeof(struct fuse_file_info)); + fi->fh = dh->fh; + fi->fh_old = dh->fh; + return dh; +} + +static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *llfi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + struct fuse_dh *dh; + struct fuse_file_info fi; + char *path; + int err; + + dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh)); + if (dh == NULL) { + reply_err(req, -ENOMEM); + return; + } + memset(dh, 0, sizeof(struct fuse_dh)); + dh->fuse = f; + dh->contents = NULL; + dh->len = 0; + dh->filled = 0; + dh->nodeid = ino; + fuse_mutex_init(&dh->lock); + + llfi->fh = (uintptr_t) dh; + + memset(&fi, 0, sizeof(fi)); + fi.flags = llfi->flags; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_opendir(f->fs, path, &fi); + fuse_finish_interrupt(f, req, &d); + dh->fh = fi.fh; + } + if (!err) { + if (fuse_reply_open(req, llfi) == -ENOENT) { + /* The opendir syscall was interrupted, so it must be cancelled */ + fuse_prepare_interrupt(f, req, &d); + fuse_fs_releasedir(f->fs, path, &fi); + fuse_finish_interrupt(f, req, &d); + pthread_mutex_destroy(&dh->lock); + free(dh); + } + } else { + reply_err(req, err); +#ifndef __SOLARIS__ + pthread_mutex_destroy(&dh->lock); +#endif /* ! __SOLARIS__ */ + free(dh); + } + free(path); + pthread_rwlock_unlock(&f->tree_lock); +} + +static int extend_contents(struct fuse_dh *dh, unsigned minsize) +{ + if (minsize > dh->size) { + char *newptr; + unsigned newsize = dh->size; + if (!newsize) + newsize = 1024; +#ifndef __SOLARIS__ + while (newsize < minsize) { + if (newsize >= 0x80000000) + newsize = 0xffffffff; + else + newsize *= 2; + } +#else /* __SOLARIS__ */ + while (newsize < minsize) + newsize *= 2; +#endif /* __SOLARIS__ */ + + newptr = (char *) realloc(dh->contents, newsize); + if (!newptr) { + dh->error = -ENOMEM; + return -1; + } + dh->contents = newptr; + dh->size = newsize; + } + return 0; +} + +static int fill_dir(void *dh_, const char *name, const struct stat *statp, + off_t off) +{ + struct fuse_dh *dh = (struct fuse_dh *) dh_; + struct stat stbuf; + size_t newlen; + + if (statp) + stbuf = *statp; + else { + memset(&stbuf, 0, sizeof(stbuf)); + stbuf.st_ino = FUSE_UNKNOWN_INO; + } + + if (!dh->fuse->conf.use_ino) { + stbuf.st_ino = FUSE_UNKNOWN_INO; + if (dh->fuse->conf.readdir_ino) { + struct node *node; + pthread_mutex_lock(&dh->fuse->lock); + node = lookup_node(dh->fuse, dh->nodeid, name); + if (node) + stbuf.st_ino = (ino_t) node->nodeid; + pthread_mutex_unlock(&dh->fuse->lock); + } + } + + if (off) { + if (extend_contents(dh, dh->needlen) == -1) + return 1; + + dh->filled = 0; + newlen = dh->len + fuse_add_direntry(dh->req, dh->contents + dh->len, + dh->needlen - dh->len, name, + &stbuf, off); + if (newlen > dh->needlen) + return 1; + } else { + newlen = dh->len + fuse_add_direntry(dh->req, NULL, 0, name, NULL, 0); + if (extend_contents(dh, newlen) == -1) + return 1; + + fuse_add_direntry(dh->req, dh->contents + dh->len, dh->size - dh->len, + name, &stbuf, newlen); + } + dh->len = newlen; + return 0; +} + +static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino, + size_t size, off_t off, struct fuse_dh *dh, + struct fuse_file_info *fi) +{ + int err = -ENOENT; + char *path; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + + dh->len = 0; + dh->error = 0; + dh->needlen = size; + dh->filled = 1; + dh->req = req; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_readdir(f->fs, path, dh, fill_dir, off, fi); + fuse_finish_interrupt(f, req, &d); + dh->req = NULL; + if (!err) + err = dh->error; + if (err) + dh->filled = 0; + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + return err; +} + +static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *llfi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_file_info fi; + struct fuse_dh *dh = get_dirhandle(llfi, &fi); + + pthread_mutex_lock(&dh->lock); + /* According to SUS, directory contents need to be refreshed on + rewinddir() */ + if (!off) + dh->filled = 0; + + if (!dh->filled) { + int err = readdir_fill(f, req, ino, size, off, dh, &fi); + if (err) { + reply_err(req, err); + goto out; + } + } + if (dh->filled) { + if (off < dh->len) { + if (off + size > dh->len) + size = dh->len - off; + } else + size = 0; + } else { + size = dh->len; + off = 0; + } + fuse_reply_buf(req, dh->contents + off, size); + out: + pthread_mutex_unlock(&dh->lock); +} + +static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *llfi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + struct fuse_file_info fi; + struct fuse_dh *dh = get_dirhandle(llfi, &fi); + char *path; + + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + fuse_prepare_interrupt(f, req, &d); + fuse_fs_releasedir(f->fs, path ? path : "-", &fi); + fuse_finish_interrupt(f, req, &d); + if (path) + free(path); + pthread_rwlock_unlock(&f->tree_lock); + pthread_mutex_lock(&dh->lock); + pthread_mutex_unlock(&dh->lock); + pthread_mutex_destroy(&dh->lock); + free(dh->contents); + free(dh); + reply_err(req, 0); +} + +static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *llfi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_file_info fi; + char *path; + int err; + + get_dirhandle(llfi, &fi); + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino) +{ + struct fuse *f = req_fuse_prepare(req); + struct statvfs buf; + char *path; + int err; + + memset(&buf, 0, sizeof(buf)); + pthread_rwlock_rdlock(&f->tree_lock); + if (!ino) { + err = -ENOMEM; + path = strdup("/"); + } else { + err = -ENOENT; + path = get_path(f, ino); + } + if (path) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_statfs(f->fs, path, &buf); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + + if (!err) + fuse_reply_statfs(req, &buf); + else + reply_err(req, err); +} + +static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_setxattr(f->fs, path, name, value, size, flags); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino, + const char *name, char *value, size_t size) +{ + int err; + char *path; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_getxattr(f->fs, path, name, value, size); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + return err; +} + +static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size) +{ + struct fuse *f = req_fuse_prepare(req); + int res; + + if (size) { + char *value = (char *) malloc(size); + if (value == NULL) { + reply_err(req, -ENOMEM); + return; + } + res = common_getxattr(f, req, ino, name, value, size); + if (res > 0) + fuse_reply_buf(req, value, res); + else + reply_err(req, res); + free(value); + } else { + res = common_getxattr(f, req, ino, name, NULL, 0); + if (res >= 0) + fuse_reply_xattr(req, res); + else + reply_err(req, res); + } +} + +static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino, + char *list, size_t size) +{ + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_listxattr(f->fs, path, list, size); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + return err; +} + +static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) +{ + struct fuse *f = req_fuse_prepare(req); + int res; + + if (size) { + char *list = (char *) malloc(size); + if (list == NULL) { + reply_err(req, -ENOMEM); + return; + } + res = common_listxattr(f, req, ino, list, size); + if (res > 0) + fuse_reply_buf(req, list, res); + else + reply_err(req, res); + free(list); + } else { + res = common_listxattr(f, req, ino, NULL, 0); + if (res >= 0) + fuse_reply_xattr(req, res); + else + reply_err(req, res); + } +} + +static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino, + const char *name) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_removexattr(f->fs, path, name); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static struct lock *locks_conflict(struct node *node, const struct lock *lock) +{ + struct lock *l; + + for (l = node->locks; l; l = l->next) + if (l->owner != lock->owner && + lock->start <= l->end && l->start <= lock->end && + (l->type == F_WRLCK || lock->type == F_WRLCK)) + break; + + return l; +} + +static void delete_lock(struct lock **lockp) +{ + struct lock *l = *lockp; + *lockp = l->next; + free(l); +} + +static void insert_lock(struct lock **pos, struct lock *lock) +{ + lock->next = *pos; + *pos = lock; +} + +static int locks_insert(struct node *node, struct lock *lock) +{ + struct lock **lp; + struct lock *newl1 = NULL; + struct lock *newl2 = NULL; + + if (lock->type != F_UNLCK || lock->start != 0 || lock->end != OFFSET_MAX) { + newl1 = malloc(sizeof(struct lock)); + newl2 = malloc(sizeof(struct lock)); + + if (!newl1 || !newl2) { + free(newl1); + free(newl2); + return -ENOLCK; + } + } + + for (lp = &node->locks; *lp;) { + struct lock *l = *lp; + if (l->owner != lock->owner) + goto skip; + + if (lock->type == l->type) { + if (l->end < lock->start - 1) + goto skip; + if (lock->end < l->start - 1) + break; + if (l->start <= lock->start && lock->end <= l->end) + goto out; + if (l->start < lock->start) + lock->start = l->start; + if (lock->end < l->end) + lock->end = l->end; + goto delete; + } else { + if (l->end < lock->start) + goto skip; + if (lock->end < l->start) + break; + if (lock->start <= l->start && l->end <= lock->end) + goto delete; + if (l->end <= lock->end) { + l->end = lock->start - 1; + goto skip; + } + if (lock->start <= l->start) { + l->start = lock->end + 1; + break; + } + *newl2 = *l; + newl2->start = lock->end + 1; + l->end = lock->start - 1; + insert_lock(&l->next, newl2); + newl2 = NULL; + } + skip: + lp = &l->next; + continue; + + delete: + delete_lock(lp); + } + if (lock->type != F_UNLCK) { + *newl1 = *lock; + insert_lock(lp, newl1); + newl1 = NULL; + } +out: + free(newl1); + free(newl2); + return 0; +} + +static void flock_to_lock(struct flock *flock, struct lock *lock) +{ + memset(lock, 0, sizeof(struct lock)); + lock->type = flock->l_type; + lock->start = flock->l_start; + lock->end = flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX; + lock->pid = flock->l_pid; +} + +static void lock_to_flock(struct lock *lock, struct flock *flock) +{ + flock->l_type = lock->type; + flock->l_start = lock->start; + flock->l_len = (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1; + flock->l_pid = lock->pid; +} + +static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino, + const char *path, struct fuse_file_info *fi) +{ + struct fuse_intr_data d; + struct flock lock; + struct lock l; + int err; + int errlock; + + fuse_prepare_interrupt(f, req, &d); + memset(&lock, 0, sizeof(lock)); + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + err = fuse_fs_flush(f->fs, path, fi); + errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock); + fuse_finish_interrupt(f, req, &d); + + if (errlock != -ENOSYS) { + flock_to_lock(&lock, &l); + l.owner = fi->lock_owner; + pthread_mutex_lock(&f->lock); + locks_insert(get_node(f, ino), &l); + pthread_mutex_unlock(&f->lock); + + /* if op.lock() is defined FLUSH is needed regardless of op.flush() */ + if (err == -ENOSYS) + err = 0; + } + return err; +} + +static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + char *path; + int err = 0; + + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (f->conf.debug) + fprintf(stderr, "RELEASE%s[%llu] flags: 0x%x\n", + fi->flush ? "+FLUSH" : "", + (unsigned long long) fi->fh, fi->flags); + + if (fi->flush) { + err = fuse_flush_common(f, req, ino, path, fi); + if (err == -ENOSYS) + err = 0; + } + + fuse_prepare_interrupt(f, req, &d); + fuse_do_release(f, ino, path, fi); + fuse_finish_interrupt(f, req, &d); + free(path); + pthread_rwlock_unlock(&f->tree_lock); + + reply_err(req, err); +} + +static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path && f->conf.debug) + fprintf(stderr, "FLUSH[%llu]\n", (unsigned long long) fi->fh); + err = fuse_flush_common(f, req, ino, path, fi); + free(path); + pthread_rwlock_unlock(&f->tree_lock); + reply_err(req, err); +} + +static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock, + int cmd) +{ + struct fuse *f = req_fuse_prepare(req); + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + struct fuse_intr_data d; + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_lock(f->fs, path, fi, cmd, lock); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + return err; +} + +static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock) +{ + int err; + struct lock l; + struct lock *conflict; + struct fuse *f = req_fuse(req); + + flock_to_lock(lock, &l); + l.owner = fi->lock_owner; + pthread_mutex_lock(&f->lock); + conflict = locks_conflict(get_node(f, ino), &l); + if (conflict) + lock_to_flock(conflict, lock); + pthread_mutex_unlock(&f->lock); + if (!conflict) + err = fuse_lock_common(req, ino, fi, lock, F_GETLK); + else + err = 0; + + if (!err) + fuse_reply_lock(req, lock); + else + reply_err(req, err); +} + +static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock, + int should_sleep) +{ + int err = fuse_lock_common(req, ino, fi, lock, should_sleep ? F_SETLKW : F_SETLK); + if (!err) { + struct fuse *f = req_fuse(req); + struct lock l; + flock_to_lock(lock, &l); + l.owner = fi->lock_owner; + pthread_mutex_lock(&f->lock); + locks_insert(get_node(f, ino), &l); + pthread_mutex_unlock(&f->lock); + } + reply_err(req, err); +} + +static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, + uint64_t idx) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + char *path; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path(f, ino); + if (path != NULL) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_bmap(f->fs, path, blocksize, &idx); + fuse_finish_interrupt(f, req, &d); + free(path); + } + pthread_rwlock_unlock(&f->tree_lock); + if (!err) + fuse_reply_bmap(req, idx); + else + reply_err(req, err); +} + +static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, + struct fuse_file_info *llfi, unsigned int flags, + const void *in_buf, size_t in_bufsz, + size_t out_bufsz) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + struct fuse_file_info fi; + char *path, *out_buf = NULL; + int err; + + err = -EPERM; + if (flags & FUSE_IOCTL_UNRESTRICTED) + goto err; + + if (flags & FUSE_IOCTL_DIR) + get_dirhandle(llfi, &fi); + else + fi = *llfi; + + if (out_bufsz) { + err = -ENOMEM; + out_buf = malloc(out_bufsz); + if (!out_buf) + goto err; + } + + assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz); + if (out_buf) + memcpy(out_buf, in_buf, in_bufsz); + + path = get_path(f, ino); /* Should be get_path_nullok() */ + if (!path) { + err = ENOENT; + goto err; + } + + fuse_prepare_interrupt(f, req, &d); + + /* Note : const qualifier dropped */ + err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags, + out_buf ? (void*)out_buf : (void*)(uintptr_t)in_buf); + + fuse_finish_interrupt(f, req, &d); + free(path); + + fuse_reply_ioctl(req, err, out_buf, out_bufsz); + goto out; +err: + reply_err(req, err); +out: + free(out_buf); +} + +static struct fuse_lowlevel_ops fuse_path_ops = { + .init = fuse_lib_init, + .destroy = fuse_lib_destroy, + .lookup = fuse_lib_lookup, + .forget = fuse_lib_forget, + .getattr = fuse_lib_getattr, + .setattr = fuse_lib_setattr, + .access = fuse_lib_access, + .readlink = fuse_lib_readlink, + .mknod = fuse_lib_mknod, + .mkdir = fuse_lib_mkdir, + .unlink = fuse_lib_unlink, + .rmdir = fuse_lib_rmdir, + .symlink = fuse_lib_symlink, + .rename = fuse_lib_rename, + .link = fuse_lib_link, + .create = fuse_lib_create, + .open = fuse_lib_open, + .read = fuse_lib_read, + .write = fuse_lib_write, + .flush = fuse_lib_flush, + .release = fuse_lib_release, + .fsync = fuse_lib_fsync, + .opendir = fuse_lib_opendir, + .readdir = fuse_lib_readdir, + .releasedir = fuse_lib_releasedir, + .fsyncdir = fuse_lib_fsyncdir, + .statfs = fuse_lib_statfs, + .setxattr = fuse_lib_setxattr, + .getxattr = fuse_lib_getxattr, + .listxattr = fuse_lib_listxattr, + .removexattr = fuse_lib_removexattr, + .getlk = fuse_lib_getlk, + .setlk = fuse_lib_setlk, + .bmap = fuse_lib_bmap, + .ioctl = fuse_lib_ioctl, +}; + +struct fuse_session *fuse_get_session(struct fuse *f) +{ + return f->se; +} + +int fuse_loop(struct fuse *f) +{ + if (f) + return fuse_session_loop(f->se); + else + return -1; +} + +void fuse_exit(struct fuse *f) +{ + fuse_session_exit(f->se); +} + +struct fuse_context *fuse_get_context(void) +{ + return &fuse_get_context_internal()->ctx; +} + +int fuse_interrupted(void) +{ + return fuse_req_interrupted(fuse_get_context_internal()->req); +} + +enum { + KEY_HELP, +}; + +#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v } + +static const struct fuse_opt fuse_lib_opts[] = { + FUSE_OPT_KEY("-h", KEY_HELP), + FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), + FUSE_LIB_OPT("debug", debug, 1), + FUSE_LIB_OPT("-d", debug, 1), + FUSE_LIB_OPT("hard_remove", hard_remove, 1), + FUSE_LIB_OPT("use_ino", use_ino, 1), + FUSE_LIB_OPT("readdir_ino", readdir_ino, 1), + FUSE_LIB_OPT("direct_io", direct_io, 1), + FUSE_LIB_OPT("kernel_cache", kernel_cache, 1), +#ifdef __SOLARIS__ + FUSE_LIB_OPT("auto_cache", auto_cache, 1), + FUSE_LIB_OPT("noauto_cache", auto_cache, 0), +#endif /* __SOLARIS__ */ + FUSE_LIB_OPT("umask=", set_mode, 1), + FUSE_LIB_OPT("umask=%o", umask, 0), + FUSE_LIB_OPT("uid=", set_uid, 1), + FUSE_LIB_OPT("uid=%d", uid, 0), + FUSE_LIB_OPT("gid=", set_gid, 1), + FUSE_LIB_OPT("gid=%d", gid, 0), + FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0), + FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0), + FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0), + FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1), + FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0), + FUSE_LIB_OPT("intr", intr, 1), + FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0), +#ifdef __SOLARIS__ + FUSE_LIB_OPT("modules=%s", modules, 0), +#endif /* __SOLARIS__ */ + FUSE_OPT_END +}; + +static void fuse_lib_help(void) +{ + fprintf(stderr, +" -o hard_remove immediate removal (don't hide files)\n" +" -o use_ino let filesystem set inode numbers\n" +" -o readdir_ino try to fill in d_ino in readdir\n" +" -o direct_io use direct I/O\n" +" -o kernel_cache cache files in kernel\n" +#ifdef __SOLARIS__ +" -o [no]auto_cache enable caching based on modification times\n" +#endif /* __SOLARIS__ */ +" -o umask=M set file permissions (octal)\n" +" -o uid=N set file owner\n" +" -o gid=N set file group\n" +" -o entry_timeout=T cache timeout for names (1.0s)\n" +" -o negative_timeout=T cache timeout for deleted names (0.0s)\n" +" -o attr_timeout=T cache timeout for attributes (1.0s)\n" +" -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n" +" -o intr allow requests to be interrupted\n" +" -o intr_signal=NUM signal to send on interrupt (%i)\n" +#ifdef __SOLARIS__ +" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n" +#endif /* __SOLARIS__ */ +"\n", FUSE_DEFAULT_INTR_SIGNAL); +} + +#ifdef __SOLARIS__ + +static void fuse_lib_help_modules(void) +{ + struct fuse_module *m; + fprintf(stderr, "\nModule options:\n"); + pthread_mutex_lock(&fuse_context_lock); + for (m = fuse_modules; m; m = m->next) { + struct fuse_fs *fs = NULL; + struct fuse_fs *newfs; + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + if (fuse_opt_add_arg(&args, "") != -1 && + fuse_opt_add_arg(&args, "-h") != -1) { + fprintf(stderr, "\n[%s]\n", m->name); + newfs = m->factory(&args, &fs); + assert(newfs == NULL); + } + fuse_opt_free_args(&args); + } + pthread_mutex_unlock(&fuse_context_lock); +} + +int fuse_is_lib_option(const char *opt) +{ + return fuse_lowlevel_is_lib_option(opt) || + fuse_opt_match(fuse_lib_opts, opt); +} + +#endif /* __SOLARIS__ */ + +static int fuse_lib_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + (void) arg; (void) outargs; + + if (key == KEY_HELP) { + struct fuse_config *conf = (struct fuse_config *) data; + fuse_lib_help(); + conf->help = 1; + } + + return 1; +} + +static int fuse_init_intr_signal(int signum, int *installed) +{ + struct sigaction old_sa; + + if (sigaction(signum, NULL, &old_sa) == -1) { + perror("fuse: cannot get old signal handler"); + return -1; + } + + if (old_sa.sa_handler == SIG_DFL) { + struct sigaction sa; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = fuse_intr_sighandler; + sigemptyset(&sa.sa_mask); + + if (sigaction(signum, &sa, NULL) == -1) { + perror("fuse: cannot set interrupt signal handler"); + return -1; + } + *installed = 1; + } + return 0; +} + +static void fuse_restore_intr_signal(int signum) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = SIG_DFL; + sigaction(signum, &sa, NULL); +} + +#ifdef __SOLARIS__ + +static int fuse_push_module(struct fuse *f, const char *module, + struct fuse_args *args) +{ + struct fuse_fs *newfs; + struct fuse_module *m = fuse_get_module(module); + struct fuse_fs *fs[2]; + + fs[0] = f->fs; + fs[1] = NULL; + if (!m) + return -1; + + newfs = m->factory(args, fs); + if (!newfs) { + fuse_put_module(m); + return -1; + } + newfs->m = m; + f->fs = newfs; + return 0; +} + +#endif /* __SOLARIS__ */ + +struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, + void *user_data) +{ + struct fuse_fs *fs; + + if (sizeof(struct fuse_operations) < op_size) { + fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n"); + op_size = sizeof(struct fuse_operations); + } + + fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs)); + if (!fs) { + fprintf(stderr, "fuse: failed to allocate fuse_fs object\n"); + return NULL; + } + + fs->user_data = user_data; + if (op) + memcpy(&fs->op, op, op_size); + return fs; +} + +struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, + const struct fuse_operations *op, size_t op_size, + void *user_data) +{ + struct fuse *f; + struct node *root; + struct fuse_fs *fs; + struct fuse_lowlevel_ops llop = fuse_path_ops; + + if (fuse_create_context_key() == -1) + goto out; + + f = (struct fuse *) calloc(1, sizeof(struct fuse)); + if (f == NULL) { + fprintf(stderr, "fuse: failed to allocate fuse object\n"); + goto out_delete_context_key; + } + + fs = fuse_fs_new(op, op_size, user_data); + if (!fs) + goto out_free; + + f->fs = fs; + + /* Oh f**k, this is ugly! */ + if (!fs->op.lock) { + llop.getlk = NULL; + llop.setlk = NULL; + } + + f->conf.entry_timeout = 1.0; + f->conf.attr_timeout = 1.0; + f->conf.negative_timeout = 0.0; + f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL; + + if (fuse_opt_parse(args, &f->conf, fuse_lib_opts, fuse_lib_opt_proc) == -1) + goto out_free_fs; + +#ifdef __SOLARIS__ + if (f->conf.modules) { + char *module; + char *next; + + for (module = f->conf.modules; module; module = next) { + char *p; + for (p = module; *p && *p != ':'; p++); + next = *p ? p + 1 : NULL; + *p = '\0'; + if (module[0] && fuse_push_module(f, module, args) == -1) + goto out_free_fs; + } + } +#endif /* __SOLARIS__ */ + + if (!f->conf.ac_attr_timeout_set) + f->conf.ac_attr_timeout = f->conf.attr_timeout; + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + /* + * In FreeBSD, we always use these settings as inode numbers are needed to + * make getcwd(3) work. + */ + f->conf.readdir_ino = 1; +#endif + + f->se = fuse_lowlevel_new(args, &llop, sizeof(llop), f); + + if (f->se == NULL) { +#ifdef __SOLARIS__ + if (f->conf.help) + fuse_lib_help_modules(); +#endif /* __SOLARIS__ */ + goto out_free_fs; + } + + fuse_session_add_chan(f->se, ch); + + f->ctr = 0; + f->generation = 0; + /* FIXME: Dynamic hash table */ + f->name_table_size = 14057; + f->name_table = (struct node **) + calloc(1, sizeof(struct node *) * f->name_table_size); + if (f->name_table == NULL) { + fprintf(stderr, "fuse: memory allocation failed\n"); + goto out_free_session; + } + + f->id_table_size = 14057; + f->id_table = (struct node **) + calloc(1, sizeof(struct node *) * f->id_table_size); + if (f->id_table == NULL) { + fprintf(stderr, "fuse: memory allocation failed\n"); + goto out_free_name_table; + } + + fuse_mutex_init(&f->lock); + pthread_rwlock_init(&f->tree_lock, NULL); + + root = (struct node *) calloc(1, sizeof(struct node)); + if (root == NULL) { + fprintf(stderr, "fuse: memory allocation failed\n"); + goto out_free_id_table; + } + + root->name = strdup("/"); + if (root->name == NULL) { + fprintf(stderr, "fuse: memory allocation failed\n"); + goto out_free_root; + } + + if (f->conf.intr && + fuse_init_intr_signal(f->conf.intr_signal, &f->intr_installed) == -1) + goto out_free_root_name; + + root->parent = NULL; + root->nodeid = FUSE_ROOT_ID; + root->generation = 0; + root->refctr = 1; + root->nlookup = 1; + hash_id(f, root); + + return f; + + out_free_root_name: + free(root->name); + out_free_root: + free(root); + out_free_id_table: + free(f->id_table); + out_free_name_table: + free(f->name_table); + out_free_session: + fuse_session_destroy(f->se); + out_free_fs: + /* Horrible compatibility hack to stop the destructor from being + called on the filesystem without init being called first */ + fs->op.destroy = NULL; + fuse_fs_destroy(f->fs); +#ifdef __SOLARIS__ + free(f->conf.modules); +#endif /* __SOLARIS__ */ + out_free: + free(f); + out_delete_context_key: + fuse_delete_context_key(); + out: + return NULL; +} + +void fuse_destroy(struct fuse *f) +{ + size_t i; + + if (f->conf.intr && f->intr_installed) + fuse_restore_intr_signal(f->conf.intr_signal); + + if (f->fs) { + struct fuse_context_i *c = fuse_get_context_internal(); + + memset(c, 0, sizeof(*c)); + c->ctx.fuse = f; + + for (i = 0; i < f->id_table_size; i++) { + struct node *node; + + for (node = f->id_table[i]; node != NULL; node = node->id_next) { + if (node->is_hidden) { + char *path = get_path(f, node->nodeid); + if (path) { + fuse_fs_unlink(f->fs, path); + free(path); + } + } + } + } + } + for (i = 0; i < f->id_table_size; i++) { + struct node *node; + struct node *next; + + for (node = f->id_table[i]; node != NULL; node = next) { + next = node->id_next; + free_node(node); + } + } + free(f->id_table); + free(f->name_table); + pthread_mutex_destroy(&f->lock); + pthread_rwlock_destroy(&f->tree_lock); + fuse_session_destroy(f->se); +#ifdef __SOLARIS__ + free(f->conf.modules); +#endif /* __SOLARIS__ */ + free(f); + fuse_delete_context_key(); +} diff --git a/libfuse-lite/fuse_i.h b/libfuse-lite/fuse_i.h new file mode 100755 index 0000000000000000000000000000000000000000..38c45c72bec23db15c1e280abd7f94b7da90cbc9 --- /dev/null +++ b/libfuse-lite/fuse_i.h @@ -0,0 +1,25 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "fuse.h" + +struct fuse_session; +struct fuse_chan; +struct fuse_lowlevel_ops; +struct fuse_req; + +struct fuse_cmd { + char *buf; + size_t buflen; + struct fuse_chan *ch; +}; + +struct fuse_chan *fuse_kern_chan_new(int fd); + +void fuse_kern_unmount(const char *mountpoint, int fd); +int fuse_kern_mount(const char *mountpoint, struct fuse_args *args); diff --git a/libfuse-lite/fuse_kern_chan.c b/libfuse-lite/fuse_kern_chan.c new file mode 100755 index 0000000000000000000000000000000000000000..e9963b5d9ca5dbaeffc6b33c4afe2dbc15219fe8 --- /dev/null +++ b/libfuse-lite/fuse_kern_chan.c @@ -0,0 +1,96 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "config.h" +#include "fuse_lowlevel.h" +#include "fuse_kernel.h" +#include "fuse_i.h" + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <assert.h> + +static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf, + size_t size) +{ + struct fuse_chan *ch = *chp; + int err; + ssize_t res; + struct fuse_session *se = fuse_chan_session(ch); + assert(se != NULL); + + restart: + res = read(fuse_chan_fd(ch), buf, size); + err = errno; + + if (fuse_session_exited(se)) + return 0; + if (res == -1) { + /* ENOENT means the operation was interrupted, it's safe + to restart */ + if (err == ENOENT) + goto restart; + + if (err == ENODEV) { + fuse_session_exit(se); + return 0; + } + /* Errors occuring during normal operation: EINTR (read + interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem + umounted) */ + if (err != EINTR && err != EAGAIN) + perror("fuse: reading device"); + return -err; + } + if ((size_t) res < sizeof(struct fuse_in_header)) { + fprintf(stderr, "short read on fuse device\n"); + return -EIO; + } + return res; +} + +static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[], + size_t count) +{ + if (iov) { + ssize_t res = writev(fuse_chan_fd(ch), iov, count); + int err = errno; + + if (res == -1) { + struct fuse_session *se = fuse_chan_session(ch); + + assert(se != NULL); + + /* ENOENT means the operation was interrupted */ + if (!fuse_session_exited(se) && err != ENOENT) + perror("fuse: writing device"); + return -err; + } + } + return 0; +} + +static void fuse_kern_chan_destroy(struct fuse_chan *ch) +{ + close(fuse_chan_fd(ch)); +} + +#define MIN_BUFSIZE 0x21000 + +struct fuse_chan *fuse_kern_chan_new(int fd) +{ + struct fuse_chan_ops op = { + .receive = fuse_kern_chan_receive, + .send = fuse_kern_chan_send, + .destroy = fuse_kern_chan_destroy, + }; + size_t bufsize = getpagesize() + 0x1000; + bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize; + return fuse_chan_new(&op, fd, bufsize, NULL); +} diff --git a/libfuse-lite/fuse_loop.c b/libfuse-lite/fuse_loop.c new file mode 100755 index 0000000000000000000000000000000000000000..0b592e5316885cfffb5558dcdeee3d888222e7da --- /dev/null +++ b/libfuse-lite/fuse_loop.c @@ -0,0 +1,40 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "config.h" +#include "fuse_lowlevel.h" + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +int fuse_session_loop(struct fuse_session *se) +{ + int res = 0; + struct fuse_chan *ch = fuse_session_next_chan(se, NULL); + size_t bufsize = fuse_chan_bufsize(ch); + char *buf = (char *) malloc(bufsize); + if (!buf) { + fprintf(stderr, "fuse: failed to allocate read buffer\n"); + return -1; + } + + while (!fuse_session_exited(se)) { + struct fuse_chan *tmpch = ch; + res = fuse_chan_recv(&tmpch, buf, bufsize); + if (res == -EINTR) + continue; + if (res <= 0) + break; + fuse_session_process(se, buf, res, tmpch); + } + + free(buf); + fuse_session_reset(se); + return res < 0 ? -1 : 0; +} diff --git a/libfuse-lite/fuse_lowlevel.c b/libfuse-lite/fuse_lowlevel.c new file mode 100755 index 0000000000000000000000000000000000000000..ee01c7c108af6600bd8de6a69f2cd9d83e71ce27 --- /dev/null +++ b/libfuse-lite/fuse_lowlevel.c @@ -0,0 +1,1434 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "config.h" +#include "fuse_lowlevel.h" +#include "fuse_kernel.h" +#include "fuse_opt.h" +#include "fuse_i.h" +#include "fuse_misc.h" +#include "fuse_lowlevel_compat.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> + +#define PARAM(inarg) (((const char *)(inarg)) + sizeof(*(inarg))) +#define OFFSET_MAX 0x7fffffffffffffffLL + +struct fuse_ll; + +struct fuse_req { + struct fuse_ll *f; + uint64_t unique; + int ctr; + pthread_mutex_t lock; + struct fuse_ctx ctx; + struct fuse_chan *ch; + int interrupted; + union { + struct { + uint64_t unique; + } i; + struct { + fuse_interrupt_func_t func; + void *data; + } ni; + } u; + struct fuse_req *next; + struct fuse_req *prev; +}; + +struct fuse_ll { + int debug; + int allow_root; + struct fuse_lowlevel_ops op; + int got_init; + void *userdata; + uid_t owner; + struct fuse_conn_info conn; + struct fuse_req list; + struct fuse_req interrupts; + pthread_mutex_t lock; + int got_destroy; +}; + +static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) +{ + attr->ino = stbuf->st_ino; + attr->mode = stbuf->st_mode; + attr->nlink = stbuf->st_nlink; + attr->uid = stbuf->st_uid; + attr->gid = stbuf->st_gid; + attr->rdev = stbuf->st_rdev; + attr->size = stbuf->st_size; + attr->blocks = stbuf->st_blocks; + attr->atime = stbuf->st_atime; + attr->mtime = stbuf->st_mtime; + attr->ctime = stbuf->st_ctime; + attr->atimensec = ST_ATIM_NSEC(stbuf); + attr->mtimensec = ST_MTIM_NSEC(stbuf); + attr->ctimensec = ST_CTIM_NSEC(stbuf); +#ifdef POSIXACLS + attr->filling = 0; /* JPA trying to be safe */ +#endif +} + +static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) +{ + stbuf->st_mode = attr->mode; + stbuf->st_uid = attr->uid; + stbuf->st_gid = attr->gid; + stbuf->st_size = attr->size; + stbuf->st_atime = attr->atime; + stbuf->st_mtime = attr->mtime; + ST_ATIM_NSEC_SET(stbuf, attr->atimensec); + ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); +} + +static size_t iov_length(const struct iovec *iov, size_t count) +{ + size_t seg; + size_t ret = 0; + + for (seg = 0; seg < count; seg++) + ret += iov[seg].iov_len; + return ret; +} + +static void list_init_req(struct fuse_req *req) +{ + req->next = req; + req->prev = req; +} + +static void list_del_req(struct fuse_req *req) +{ + struct fuse_req *prev = req->prev; + struct fuse_req *next = req->next; + prev->next = next; + next->prev = prev; +} + +static void list_add_req(struct fuse_req *req, struct fuse_req *next) +{ + struct fuse_req *prev = next->prev; + req->next = next; + req->prev = prev; + prev->next = req; + next->prev = req; +} + +static void destroy_req(fuse_req_t req) +{ + pthread_mutex_destroy(&req->lock); + free(req); +} + +static void free_req(fuse_req_t req) +{ + int ctr; + struct fuse_ll *f = req->f; + + pthread_mutex_lock(&req->lock); + req->u.ni.func = NULL; + req->u.ni.data = NULL; + pthread_mutex_unlock(&req->lock); + + pthread_mutex_lock(&f->lock); + list_del_req(req); + ctr = --req->ctr; + pthread_mutex_unlock(&f->lock); + if (!ctr) + destroy_req(req); +} + +static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, + int count) +{ + struct fuse_out_header out; + int res; + + if (error <= -1000 || error > 0) { + fprintf(stderr, "fuse: bad error value: %i\n", error); + error = -ERANGE; + } + + out.unique = req->unique; + out.error = error; + iov[0].iov_base = &out; + iov[0].iov_len = sizeof(struct fuse_out_header); + out.len = iov_length(iov, count); + + if (req->f->debug) + fprintf(stderr, " unique: %llu, error: %i (%s), outsize: %i\n", + (unsigned long long) out.unique, out.error, + strerror(-out.error), out.len); + res = fuse_chan_send(req->ch, iov, count); + free_req(req); + + return res; +} + +static int send_reply(fuse_req_t req, int error, const void *arg, + size_t argsize) +{ + struct iovec iov[2]; + int count = 1; + if (argsize) { + /* Note : const qualifier dropped */ + iov[1].iov_base = (void *)(uintptr_t) arg; + iov[1].iov_len = argsize; + count++; + } + return send_reply_iov(req, error, iov, count); +} + +#if 0 /* not used */ +int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count) +{ + int res; + struct iovec *padded_iov; + + padded_iov = malloc((count + 1) * sizeof(struct iovec)); + if (padded_iov == NULL) + return fuse_reply_err(req, -ENOMEM); + + memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); + count++; + + res = send_reply_iov(req, 0, padded_iov, count); + free(padded_iov); + + return res; +} +#endif + +size_t fuse_dirent_size(size_t namelen) +{ + return FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + namelen); +} + +char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf, + off_t off) +{ + unsigned namelen = strlen(name); + unsigned entlen = FUSE_NAME_OFFSET + namelen; + unsigned entsize = fuse_dirent_size(namelen); + unsigned padlen = entsize - entlen; + struct fuse_dirent *dirent = (struct fuse_dirent *) buf; + + dirent->ino = stbuf->st_ino; + dirent->off = off; + dirent->namelen = namelen; + dirent->type = (stbuf->st_mode & 0170000) >> 12; + strncpy(dirent->name, name, namelen); + if (padlen) + memset(buf + entlen, 0, padlen); + + return buf + entsize; +} + +size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, + const char *name, const struct stat *stbuf, off_t off) +{ + size_t entsize; + + (void) req; + entsize = fuse_dirent_size(strlen(name)); + if (entsize <= bufsize && buf) + fuse_add_dirent(buf, name, stbuf, off); + return entsize; +} + +static void convert_statfs(const struct statvfs *stbuf, + struct fuse_kstatfs *kstatfs) +{ + kstatfs->bsize = stbuf->f_bsize; + kstatfs->frsize = stbuf->f_frsize; + kstatfs->blocks = stbuf->f_blocks; + kstatfs->bfree = stbuf->f_bfree; + kstatfs->bavail = stbuf->f_bavail; + kstatfs->files = stbuf->f_files; + kstatfs->ffree = stbuf->f_ffree; + kstatfs->namelen = stbuf->f_namemax; +} + +static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) +{ + return send_reply(req, 0, arg, argsize); +} + +int fuse_reply_err(fuse_req_t req, int err) +{ + return send_reply(req, -err, NULL, 0); +} + +void fuse_reply_none(fuse_req_t req) +{ + fuse_chan_send(req->ch, NULL, 0); + free_req(req); +} + +static unsigned long calc_timeout_sec(double t) +{ + if (t > (double) ULONG_MAX) + return ULONG_MAX; + else if (t < 0.0) + return 0; + else + return (unsigned long) t; +} + +static unsigned int calc_timeout_nsec(double t) +{ + unsigned long secs = calc_timeout_sec(t); + double f = t - (double)secs; + if (f < 0.0) + return 0; + else if (f >= 0.999999999) + return 999999999; + else + return (unsigned int) (f * 1.0e9); +} + +static void fill_entry(struct fuse_entry_out *arg, + const struct fuse_entry_param *e) +{ + arg->nodeid = e->ino; + arg->generation = e->generation; + arg->entry_valid = calc_timeout_sec(e->entry_timeout); + arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); + arg->attr_valid = calc_timeout_sec(e->attr_timeout); + arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); + convert_stat(&e->attr, &arg->attr); +} + +static void fill_open(struct fuse_open_out *arg, + const struct fuse_file_info *f) +{ + arg->fh = f->fh; + if (f->direct_io) + arg->open_flags |= FOPEN_DIRECT_IO; + if (f->keep_cache) + arg->open_flags |= FOPEN_KEEP_CACHE; +} + +int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) +{ + struct fuse_entry_out arg; + + /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant + negative entry */ + if (!e->ino && req->f->conn.proto_minor < 4) + return fuse_reply_err(req, ENOENT); + + memset(&arg, 0, sizeof(arg)); + fill_entry(&arg, e); + return send_reply_ok(req, &arg, (req->f->conn.proto_minor >= 12 + ? sizeof(arg) : FUSE_COMPAT_ENTRY_OUT_SIZE)); +} + +int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, + const struct fuse_file_info *f) +{ + struct { + struct fuse_entry_out e; + struct fuse_open_out o; + } arg; + + memset(&arg, 0, sizeof(arg)); + fill_entry(&arg.e, e); + if (req->f->conn.proto_minor < 12) { + fill_open((struct fuse_open_out*) + ((char*)&arg + FUSE_COMPAT_ENTRY_OUT_SIZE), f); + return send_reply_ok(req, &arg, + FUSE_COMPAT_ENTRY_OUT_SIZE + sizeof(struct fuse_open_out)); + } else { + fill_open(&arg.o, f); + return send_reply_ok(req, &arg, sizeof(arg)); + } +} + +int fuse_reply_attr(fuse_req_t req, const struct stat *attr, + double attr_timeout) +{ + struct fuse_attr_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.attr_valid = calc_timeout_sec(attr_timeout); + arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); + convert_stat(attr, &arg.attr); + + return send_reply_ok(req, &arg, (req->f->conn.proto_minor >= 12 + ? sizeof(arg) : FUSE_COMPAT_FUSE_ATTR_OUT_SIZE)); +} + +int fuse_reply_readlink(fuse_req_t req, const char *linkname) +{ + return send_reply_ok(req, linkname, strlen(linkname)); +} + +int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) +{ + struct fuse_open_out arg; + + memset(&arg, 0, sizeof(arg)); + fill_open(&arg, f); + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_write(fuse_req_t req, size_t count) +{ + struct fuse_write_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.size = count; + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size) +{ + return send_reply_ok(req, buf, size); +} + +int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) +{ + struct fuse_statfs_out arg; + size_t size = req->f->conn.proto_minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(arg); + + memset(&arg, 0, sizeof(arg)); + convert_statfs(stbuf, &arg.st); + + return send_reply_ok(req, &arg, size); +} + +int fuse_reply_xattr(fuse_req_t req, size_t count) +{ + struct fuse_getxattr_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.size = count; + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_lock(fuse_req_t req, struct flock *lock) +{ + struct fuse_lk_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.lk.type = lock->l_type; + if (lock->l_type != F_UNLCK) { + arg.lk.start = lock->l_start; + if (lock->l_len == 0) + arg.lk.end = OFFSET_MAX; + else + arg.lk.end = lock->l_start + lock->l_len - 1; + } + arg.lk.pid = lock->l_pid; + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_bmap(fuse_req_t req, uint64_t idx) +{ + struct fuse_bmap_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.block = idx; + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size) +{ + struct fuse_ioctl_out arg; + struct iovec iov[3]; + size_t count = 1; + + memset(&arg, 0, sizeof(arg)); + arg.result = result; + iov[count].iov_base = &arg; + iov[count].iov_len = sizeof(arg); + count++; + + if (size) { + /* Note : const qualifier dropped */ + iov[count].iov_base = (char *)(uintptr_t) buf; + iov[count].iov_len = size; + count++; + } + + return send_reply_iov(req, 0, iov, count); +} + +static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const char *name = (const char *) inarg; + + if (req->f->op.lookup) + req->f->op.lookup(req, nodeid, name); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_forget_in *arg = (const struct fuse_forget_in *) inarg; + + if (req->f->op.forget) + req->f->op.forget(req, nodeid, arg->nlookup); + else + fuse_reply_none(req); +} + +static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + (void) inarg; + + if (req->f->op.getattr) + req->f->op.getattr(req, nodeid, NULL); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_setattr_in *arg = (const struct fuse_setattr_in *) inarg; + + if (req->f->op.setattr) { + struct fuse_file_info *fi = NULL; + struct fuse_file_info fi_store; + struct stat stbuf; + memset(&stbuf, 0, sizeof(stbuf)); + convert_attr(arg, &stbuf); + if (arg->valid & FATTR_FH) { + memset(&fi_store, 0, sizeof(fi_store)); + fi = &fi_store; + fi->fh = arg->fh; + fi->fh_old = fi->fh; + } + req->f->op.setattr(req, nodeid, &stbuf, arg->valid & ~FATTR_FH, fi); + } else + fuse_reply_err(req, ENOSYS); +} + +static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_access_in *arg = (const struct fuse_access_in *) inarg; + + if (req->f->op.access) + req->f->op.access(req, nodeid, arg->mask); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + (void) inarg; + + if (req->f->op.readlink) + req->f->op.readlink(req, nodeid); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_mknod_in *arg = (const struct fuse_mknod_in *) inarg; + const char *name = PARAM(arg); + + if (req->f->conn.proto_minor >= 12) + req->ctx.umask = arg->umask; + else + name = (const char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE; + + if (req->f->op.mknod) + req->f->op.mknod(req, nodeid, name, arg->mode, arg->rdev); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_mkdir_in *arg = (const struct fuse_mkdir_in *) inarg; + + if (req->f->conn.proto_minor >= 12) + req->ctx.umask = arg->umask; + + if (req->f->op.mkdir) + req->f->op.mkdir(req, nodeid, PARAM(arg), arg->mode); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const char *name = (const char *) inarg; + + if (req->f->op.unlink) + req->f->op.unlink(req, nodeid, name); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const char *name = (const char *) inarg; + + if (req->f->op.rmdir) + req->f->op.rmdir(req, nodeid, name); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const char *name = (const char *) inarg; + const char *linkname = ((const char *) inarg) + strlen((const char *) inarg) + 1; + + if (req->f->op.symlink) + req->f->op.symlink(req, linkname, nodeid, name); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_rename_in *arg = (const struct fuse_rename_in *) inarg; + const char *oldname = PARAM(arg); + const char *newname = oldname + strlen(oldname) + 1; + + if (req->f->op.rename) + req->f->op.rename(req, nodeid, oldname, arg->newdir, newname); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_link_in *arg = (const struct fuse_link_in *) inarg; + + if (req->f->op.link) + req->f->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_create_in *arg = (const struct fuse_create_in *) inarg; + + if (req->f->op.create) { + struct fuse_file_info fi; + const char *name = PARAM(arg); + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + + if (req->f->conn.proto_minor >= 12) + req->ctx.umask = arg->umask; + else + name = (const char *) inarg + sizeof(struct fuse_open_in); + + req->f->op.create(req, nodeid, name, arg->mode, &fi); + } else + fuse_reply_err(req, ENOSYS); +} + +static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_open_in *arg = (const struct fuse_open_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + + if (req->f->op.open) + req->f->op.open(req, nodeid, &fi); + else + fuse_reply_open(req, &fi); +} + +static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_read_in *arg = (const struct fuse_read_in *) inarg; + + if (req->f->op.read) { + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + req->f->op.read(req, nodeid, arg->size, arg->offset, &fi); + } else + fuse_reply_err(req, ENOSYS); +} + +static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_write_in *arg = (const struct fuse_write_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + fi.writepage = arg->write_flags & 1; + + if (req->f->op.write) { + const char *buf; + + if (req->f->conn.proto_minor >= 12) + buf = PARAM(arg); + else + buf = ((const char*)arg) + FUSE_COMPAT_WRITE_IN_SIZE; + req->f->op.write(req, nodeid, buf, arg->size, arg->offset, &fi); + } else + fuse_reply_err(req, ENOSYS); +} + +static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_flush_in *arg = (const struct fuse_flush_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + fi.flush = 1; + if (req->f->conn.proto_minor >= 7) + fi.lock_owner = arg->lock_owner; + + if (req->f->op.flush) + req->f->op.flush(req, nodeid, &fi); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_release_in *arg = (const struct fuse_release_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + fi.fh = arg->fh; + fi.fh_old = fi.fh; + if (req->f->conn.proto_minor >= 8) { + fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; + fi.lock_owner = arg->lock_owner; + } + + if (req->f->op.release) + req->f->op.release(req, nodeid, &fi); + else + fuse_reply_err(req, 0); +} + +static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_fsync_in *arg = (const struct fuse_fsync_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + + if (req->f->op.fsync) + req->f->op.fsync(req, nodeid, arg->fsync_flags & 1, &fi); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_open_in *arg = (const struct fuse_open_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + + if (req->f->op.opendir) + req->f->op.opendir(req, nodeid, &fi); + else + fuse_reply_open(req, &fi); +} + +static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_read_in *arg = (const struct fuse_read_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + + if (req->f->op.readdir) + req->f->op.readdir(req, nodeid, arg->size, arg->offset, &fi); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_release_in *arg = (const struct fuse_release_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + fi.fh = arg->fh; + fi.fh_old = fi.fh; + + if (req->f->op.releasedir) + req->f->op.releasedir(req, nodeid, &fi); + else + fuse_reply_err(req, 0); +} + +static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_fsync_in *arg = (const struct fuse_fsync_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.fh_old = fi.fh; + + if (req->f->op.fsyncdir) + req->f->op.fsyncdir(req, nodeid, arg->fsync_flags & 1, &fi); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + (void) nodeid; + (void) inarg; + + if (req->f->op.statfs) + req->f->op.statfs(req, nodeid); + else { + struct statvfs buf = { + .f_namemax = 255, + .f_bsize = 512, + }; + fuse_reply_statfs(req, &buf); + } +} + +static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_setxattr_in *arg = (const struct fuse_setxattr_in *) inarg; + const char *name = PARAM(arg); + const char *value = name + strlen(name) + 1; + + if (req->f->op.setxattr) + req->f->op.setxattr(req, nodeid, name, value, arg->size, arg->flags); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_getxattr_in *arg = (const struct fuse_getxattr_in *) inarg; + + if (req->f->op.getxattr) + req->f->op.getxattr(req, nodeid, PARAM(arg), arg->size); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_getxattr_in *arg = (const struct fuse_getxattr_in *) inarg; + + if (req->f->op.listxattr) + req->f->op.listxattr(req, nodeid, arg->size); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const char *name = (const char *) inarg; + + if (req->f->op.removexattr) + req->f->op.removexattr(req, nodeid, name); + else + fuse_reply_err(req, ENOSYS); +} + +static void convert_fuse_file_lock(const struct fuse_file_lock *fl, + struct flock *flock) +{ + memset(flock, 0, sizeof(struct flock)); + flock->l_type = fl->type; + flock->l_whence = SEEK_SET; + flock->l_start = fl->start; + if (fl->end == OFFSET_MAX) + flock->l_len = 0; + else + flock->l_len = fl->end - fl->start + 1; + flock->l_pid = fl->pid; +} + +static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_lk_in *arg = (const struct fuse_lk_in *) inarg; + struct fuse_file_info fi; + struct flock flock; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.lock_owner = arg->owner; + + convert_fuse_file_lock(&arg->lk, &flock); + if (req->f->op.getlk) + req->f->op.getlk(req, nodeid, &fi, &flock); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, + const void *inarg, int should_sleep) +{ + const struct fuse_lk_in *arg = (const struct fuse_lk_in *) inarg; + struct fuse_file_info fi; + struct flock flock; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.lock_owner = arg->owner; + + convert_fuse_file_lock(&arg->lk, &flock); + if (req->f->op.setlk) + req->f->op.setlk(req, nodeid, &fi, &flock, should_sleep); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + do_setlk_common(req, nodeid, inarg, 0); +} + +static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + do_setlk_common(req, nodeid, inarg, 1); +} + +static int find_interrupted(struct fuse_ll *f, struct fuse_req *req) +{ + struct fuse_req *curr; + + for (curr = f->list.next; curr != &f->list; curr = curr->next) { + if (curr->unique == req->u.i.unique) { + curr->ctr++; + pthread_mutex_unlock(&f->lock); + + /* Ugh, ugly locking */ + pthread_mutex_lock(&curr->lock); + pthread_mutex_lock(&f->lock); + curr->interrupted = 1; + pthread_mutex_unlock(&f->lock); + if (curr->u.ni.func) + curr->u.ni.func(curr, curr->u.ni.data); + pthread_mutex_unlock(&curr->lock); + + pthread_mutex_lock(&f->lock); + curr->ctr--; + if (!curr->ctr) + destroy_req(curr); + + return 1; + } + } + for (curr = f->interrupts.next; curr != &f->interrupts; + curr = curr->next) { + if (curr->u.i.unique == req->u.i.unique) + return 1; + } + return 0; +} + +static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_interrupt_in *arg = (const struct fuse_interrupt_in *) inarg; + struct fuse_ll *f = req->f; + + (void) nodeid; + if (f->debug) + fprintf(stderr, "INTERRUPT: %llu\n", (unsigned long long) arg->unique); + + req->u.i.unique = arg->unique; + + pthread_mutex_lock(&f->lock); + if (find_interrupted(f, req)) + destroy_req(req); + else + list_add_req(req, &f->interrupts); + pthread_mutex_unlock(&f->lock); +} + +static struct fuse_req *check_interrupt(struct fuse_ll *f, struct fuse_req *req) +{ + struct fuse_req *curr; + + for (curr = f->interrupts.next; curr != &f->interrupts; curr = curr->next) { + if (curr->u.i.unique == req->unique) { + req->interrupted = 1; + list_del_req(curr); + free(curr); + return NULL; + } + } + curr = f->interrupts.next; + if (curr != &f->interrupts) { + list_del_req(curr); + list_init_req(curr); + return curr; + } else + return NULL; +} + +static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_bmap_in *arg = (const struct fuse_bmap_in *) inarg; + + if (req->f->op.bmap) + req->f->op.bmap(req, nodeid, arg->blocksize, arg->block); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_ioctl_in *arg = (const struct fuse_ioctl_in *) inarg; + unsigned int flags = arg->flags; + const void *in_buf = arg->in_size ? PARAM(arg) : NULL; + struct fuse_file_info fi; + + if (flags & FUSE_IOCTL_DIR && + !(req->f->conn.want & FUSE_CAP_IOCTL_DIR)) { + fuse_reply_err(req, ENOTTY); + return; + } + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + +/* TODO JPA (need req->ioctl_64bit in obscure fuse_req_t) +// probably a 64 bit ioctl on a 32-bit cpu +// this is to forward a request from the kernel + if (sizeof(void *) == 4 && req->f->conn.proto_minor >= 16 && + !(flags & FUSE_IOCTL_32BIT)) { + req->ioctl_64bit = 1; + } +*/ + + if (req->f->op.ioctl) + req->f->op.ioctl(req, nodeid, arg->cmd, + (void *)(uintptr_t)arg->arg, &fi, flags, + in_buf, arg->in_size, arg->out_size); + else + fuse_reply_err(req, ENOSYS); +} + +static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_init_in *arg = (const struct fuse_init_in *) inarg; + struct fuse_init_out outarg; + struct fuse_ll *f = req->f; + size_t bufsize = fuse_chan_bufsize(req->ch); + + (void) nodeid; + if (f->debug) { + fprintf(stderr, "INIT: %u.%u\n", arg->major, arg->minor); + if (arg->major > 7 || (arg->major == 7 && arg->minor >= 6)) { + fprintf(stderr, "flags=0x%08x\n", arg->flags); + fprintf(stderr, "max_readahead=0x%08x\n", arg->max_readahead); + } + } + f->conn.proto_major = arg->major; + f->conn.proto_minor = arg->minor; + + if (arg->major < 7) { + fprintf(stderr, "fuse: unsupported protocol version: %u.%u\n", + arg->major, arg->minor); + fuse_reply_err(req, EPROTO); + return; + } + + if (arg->major > 7 || (arg->major == 7 && arg->minor >= 6)) { + if (f->conn.async_read) + f->conn.async_read = arg->flags & FUSE_ASYNC_READ; + if (arg->max_readahead < f->conn.max_readahead) + f->conn.max_readahead = arg->max_readahead; +#ifdef POSIXACLS + if (arg->flags & FUSE_DONT_MASK) + f->conn.capable |= FUSE_CAP_DONT_MASK; +#endif + if (arg->flags & FUSE_BIG_WRITES) + f->conn.capable |= FUSE_CAP_BIG_WRITES; + if (arg->flags & FUSE_HAS_IOCTL_DIR) + f->conn.capable |= FUSE_CAP_IOCTL_DIR; + } else { + f->conn.async_read = 0; + f->conn.max_readahead = 0; + } + + if (bufsize < FUSE_MIN_READ_BUFFER) { + fprintf(stderr, "fuse: warning: buffer size too small: %zu\n", + bufsize); + bufsize = FUSE_MIN_READ_BUFFER; + } + + bufsize -= 4096; + if (bufsize < f->conn.max_write) + f->conn.max_write = bufsize; + + f->got_init = 1; + if (f->op.init) + f->op.init(f->userdata, &f->conn); + + memset(&outarg, 0, sizeof(outarg)); + outarg.major = FUSE_KERNEL_VERSION; + /* + * Suggest using protocol 7.18 when available, and fallback + * to 7.12 or even earlier when running on an old kernel. + * Protocol 7.12 has the ability to process the umask + * conditionnally (as needed if POSIXACLS is set) + * Protocol 7.18 has the ability to process the ioctls + */ + if (arg->major > 7 || (arg->major == 7 && arg->minor >= 18)) { + outarg.minor = FUSE_KERNEL_MINOR_VERSION; + if (f->conn.want & FUSE_CAP_IOCTL_DIR) + outarg.flags |= FUSE_HAS_IOCTL_DIR; +#ifdef POSIXACLS + if (f->conn.want & FUSE_CAP_DONT_MASK) + outarg.flags |= FUSE_DONT_MASK; +#endif + } else { + /* Never use a version more recent than supported by the kernel */ + if ((arg->major < FUSE_KERNEL_MAJOR_FALLBACK) + || ((arg->major == FUSE_KERNEL_MAJOR_FALLBACK) + && (arg->minor < FUSE_KERNEL_MINOR_FALLBACK))) { + outarg.major = arg->major; + outarg.minor = arg->minor; + } else { + outarg.major = FUSE_KERNEL_MAJOR_FALLBACK; + outarg.minor = FUSE_KERNEL_MINOR_FALLBACK; +#ifdef POSIXACLS + if (f->conn.want & FUSE_CAP_DONT_MASK) + outarg.flags |= FUSE_DONT_MASK; +#endif + } + } + if (f->conn.async_read) + outarg.flags |= FUSE_ASYNC_READ; + if (f->op.getlk && f->op.setlk) + outarg.flags |= FUSE_POSIX_LOCKS; + if (f->conn.want & FUSE_CAP_BIG_WRITES) + outarg.flags |= FUSE_BIG_WRITES; + outarg.max_readahead = f->conn.max_readahead; + outarg.max_write = f->conn.max_write; + + if (f->debug) { + fprintf(stderr, " INIT: %u.%u\n", outarg.major, outarg.minor); + fprintf(stderr, " flags=0x%08x\n", outarg.flags); + fprintf(stderr, " max_readahead=0x%08x\n", outarg.max_readahead); + fprintf(stderr, " max_write=0x%08x\n", outarg.max_write); + } + + send_reply_ok(req, &outarg, arg->minor < 5 ? 8 : sizeof(outarg)); +} + +static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_ll *f = req->f; + + (void) nodeid; + (void) inarg; + + f->got_destroy = 1; + if (f->op.destroy) + f->op.destroy(f->userdata); + + send_reply_ok(req, NULL, 0); +} + +void *fuse_req_userdata(fuse_req_t req) +{ + return req->f->userdata; +} + +const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) +{ + return &req->ctx; +} + +void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, + void *data) +{ + pthread_mutex_lock(&req->lock); + req->u.ni.func = func; + req->u.ni.data = data; + if (req->interrupted && func) + func(req, data); + pthread_mutex_unlock(&req->lock); +} + +int fuse_req_interrupted(fuse_req_t req) +{ + int interrupted; + + pthread_mutex_lock(&req->f->lock); + interrupted = req->interrupted; + pthread_mutex_unlock(&req->f->lock); + + return interrupted; +} + +static struct { + void (*func)(fuse_req_t, fuse_ino_t, const void *); + const char *name; +} fuse_ll_ops[] = { + [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, + [FUSE_FORGET] = { do_forget, "FORGET" }, + [FUSE_GETATTR] = { do_getattr, "GETATTR" }, + [FUSE_SETATTR] = { do_setattr, "SETATTR" }, + [FUSE_READLINK] = { do_readlink, "READLINK" }, + [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, + [FUSE_MKNOD] = { do_mknod, "MKNOD" }, + [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, + [FUSE_UNLINK] = { do_unlink, "UNLINK" }, + [FUSE_RMDIR] = { do_rmdir, "RMDIR" }, + [FUSE_RENAME] = { do_rename, "RENAME" }, + [FUSE_LINK] = { do_link, "LINK" }, + [FUSE_OPEN] = { do_open, "OPEN" }, + [FUSE_READ] = { do_read, "READ" }, + [FUSE_WRITE] = { do_write, "WRITE" }, + [FUSE_STATFS] = { do_statfs, "STATFS" }, + [FUSE_RELEASE] = { do_release, "RELEASE" }, + [FUSE_FSYNC] = { do_fsync, "FSYNC" }, + [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" }, + [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" }, + [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" }, + [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" }, + [FUSE_FLUSH] = { do_flush, "FLUSH" }, + [FUSE_INIT] = { do_init, "INIT" }, + [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, + [FUSE_READDIR] = { do_readdir, "READDIR" }, + [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, + [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, + [FUSE_GETLK] = { do_getlk, "GETLK" }, + [FUSE_SETLK] = { do_setlk, "SETLK" }, + [FUSE_SETLKW] = { do_setlkw, "SETLKW" }, + [FUSE_ACCESS] = { do_access, "ACCESS" }, + [FUSE_CREATE] = { do_create, "CREATE" }, + [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, + [FUSE_BMAP] = { do_bmap, "BMAP" }, + [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, + [FUSE_DESTROY] = { do_destroy, "DESTROY" }, +}; + +#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) + +static const char *opname(enum fuse_opcode opcode) +{ + if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) + return "???"; + else + return fuse_ll_ops[opcode].name; +} + +static void fuse_ll_process(void *data, const char *buf, size_t len, + struct fuse_chan *ch) +{ + struct fuse_ll *f = (struct fuse_ll *) data; + const struct fuse_in_header *in = (const struct fuse_in_header *) buf; + const void *inarg = buf + sizeof(struct fuse_in_header); + struct fuse_req *req; + + if (f->debug) + fprintf(stderr, "unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %zu\n", + (unsigned long long) in->unique, + opname((enum fuse_opcode) in->opcode), in->opcode, + (unsigned long) in->nodeid, len); + + req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req)); + if (req == NULL) { + fprintf(stderr, "fuse: failed to allocate request\n"); + return; + } + + req->f = f; + req->unique = in->unique; + req->ctx.uid = in->uid; + req->ctx.gid = in->gid; + req->ctx.pid = in->pid; + req->ch = ch; + req->ctr = 1; + list_init_req(req); + fuse_mutex_init(&req->lock); + + if (!f->got_init && in->opcode != FUSE_INIT) + fuse_reply_err(req, EIO); + else if (f->allow_root && in->uid != f->owner && in->uid != 0 && + in->opcode != FUSE_INIT && in->opcode != FUSE_READ && + in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC && + in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && + in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR) { + fuse_reply_err(req, EACCES); + } else if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) + fuse_reply_err(req, ENOSYS); + else { + if (in->opcode != FUSE_INTERRUPT) { + struct fuse_req *intr; + pthread_mutex_lock(&f->lock); + intr = check_interrupt(f, req); + list_add_req(req, &f->list); + pthread_mutex_unlock(&f->lock); + if (intr) + fuse_reply_err(intr, EAGAIN); + } + fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); + } +} + +enum { + KEY_HELP, + KEY_VERSION, +}; + +static struct fuse_opt fuse_ll_opts[] = { + { "debug", offsetof(struct fuse_ll, debug), 1 }, + { "-d", offsetof(struct fuse_ll, debug), 1 }, + { "allow_root", offsetof(struct fuse_ll, allow_root), 1 }, + { "max_write=%u", offsetof(struct fuse_ll, conn.max_write), 0 }, + { "max_readahead=%u", offsetof(struct fuse_ll, conn.max_readahead), 0 }, + { "async_read", offsetof(struct fuse_ll, conn.async_read), 1 }, + { "sync_read", offsetof(struct fuse_ll, conn.async_read), 0 }, + FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD), + FUSE_OPT_KEY("-h", KEY_HELP), + FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_OPT_KEY("-V", KEY_VERSION), + FUSE_OPT_KEY("--version", KEY_VERSION), + FUSE_OPT_END +}; + +static void fuse_ll_version(void) +{ + fprintf(stderr, "using FUSE kernel interface version %i.%i\n", + FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); +} + +static void fuse_ll_help(void) +{ + fprintf(stderr, +" -o max_write=N set maximum size of write requests\n" +" -o max_readahead=N set maximum readahead\n" +" -o async_read perform reads asynchronously (default)\n" +" -o sync_read perform reads synchronously\n"); +} + +static int fuse_ll_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + (void) data; (void) outargs; + + switch (key) { + case KEY_HELP: + fuse_ll_help(); + break; + + case KEY_VERSION: + fuse_ll_version(); + break; + + default: + fprintf(stderr, "fuse: unknown option `%s'\n", arg); + } + + return -1; +} + +#ifdef __SOLARIS__ + +int fuse_lowlevel_is_lib_option(const char *opt) +{ + return fuse_opt_match(fuse_ll_opts, opt); +} + +#endif /* __SOLARIS__ */ + +static void fuse_ll_destroy(void *data) +{ + struct fuse_ll *f = (struct fuse_ll *) data; + + if (f->got_init && !f->got_destroy) { + if (f->op.destroy) + f->op.destroy(f->userdata); + } + + pthread_mutex_destroy(&f->lock); + free(f); +} + +struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, + const struct fuse_lowlevel_ops *op, + size_t op_size, void *userdata) +{ + struct fuse_ll *f; + struct fuse_session *se; + struct fuse_session_ops sop = { + .process = fuse_ll_process, + .destroy = fuse_ll_destroy, + }; + + if (sizeof(struct fuse_lowlevel_ops) < op_size) { + fprintf(stderr, "fuse: warning: library too old, some operations may not work\n"); + op_size = sizeof(struct fuse_lowlevel_ops); + } + + f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll)); + if (f == NULL) { + fprintf(stderr, "fuse: failed to allocate fuse object\n"); + goto out; + } + + f->conn.async_read = 1; + f->conn.max_write = UINT_MAX; + f->conn.max_readahead = UINT_MAX; + list_init_req(&f->list); + list_init_req(&f->interrupts); + fuse_mutex_init(&f->lock); + + if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1) + goto out_free; + + memcpy(&f->op, op, op_size); + f->owner = getuid(); + f->userdata = userdata; + + se = fuse_session_new(&sop, f); + if (!se) + goto out_free; + + return se; + + out_free: + free(f); + out: + return NULL; +} diff --git a/libfuse-lite/fuse_misc.h b/libfuse-lite/fuse_misc.h new file mode 100755 index 0000000000000000000000000000000000000000..86309896fd88ac72e882218b0eaad45f396488d5 --- /dev/null +++ b/libfuse-lite/fuse_misc.h @@ -0,0 +1,51 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "config.h" +#include <pthread.h> + +#ifndef USE_UCLIBC +#define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL) +#else +static inline void fuse_mutex_init(pthread_mutex_t *mut) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); + pthread_mutex_init(mut, &attr); + pthread_mutexattr_destroy(&attr); +} +#endif + +#ifdef HAVE_STRUCT_STAT_ST_ATIM +/* Linux */ +#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec) +#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec) +#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec) +#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val) +#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val) +#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC) +/* FreeBSD */ +#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec) +#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec) +#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec) +#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val) +#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val) +#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC) +#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimensec) +#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimensec) +#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimensec) +#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimensec = (val) +#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimensec = (val) +#else +#define ST_ATIM_NSEC(stbuf) 0 +#define ST_CTIM_NSEC(stbuf) 0 +#define ST_MTIM_NSEC(stbuf) 0 +#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0) +#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0) +#endif diff --git a/libfuse-lite/fuse_opt.c b/libfuse-lite/fuse_opt.c new file mode 100755 index 0000000000000000000000000000000000000000..80b07774812126be6259f71b97b4e43b4ca9977d --- /dev/null +++ b/libfuse-lite/fuse_opt.c @@ -0,0 +1,382 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "config.h" +#include "fuse_opt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +struct fuse_opt_context { + void *data; + const struct fuse_opt *opt; + fuse_opt_proc_t proc; + int argctr; + int argc; + char **argv; + struct fuse_args outargs; + char *opts; + int nonopt; +}; + +void fuse_opt_free_args(struct fuse_args *args) +{ + if (args) { + if (args->argv && args->allocated) { + int i; + for (i = 0; i < args->argc; i++) + free(args->argv[i]); + free(args->argv); + } + args->argc = 0; + args->argv = NULL; + args->allocated = 0; + } +} + +static int alloc_failed(void) +{ + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; +} + +int fuse_opt_add_arg(struct fuse_args *args, const char *arg) +{ + char **newargv; + char *newarg; + + assert(!args->argv || args->allocated); + + newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); + newarg = newargv ? strdup(arg) : NULL; + if (!newargv || !newarg) + return alloc_failed(); + + args->argv = newargv; + args->allocated = 1; + args->argv[args->argc++] = newarg; + args->argv[args->argc] = NULL; + return 0; +} + +int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) +{ + assert(pos <= args->argc); + if (fuse_opt_add_arg(args, arg) == -1) + return -1; + + if (pos != args->argc - 1) { + char *newarg = args->argv[args->argc - 1]; + memmove(&args->argv[pos + 1], &args->argv[pos], + sizeof(char *) * (args->argc - pos - 1)); + args->argv[pos] = newarg; + } + return 0; +} + +static int next_arg(struct fuse_opt_context *ctx, const char *opt) +{ + if (ctx->argctr + 1 >= ctx->argc) { + fprintf(stderr, "fuse: missing argument after `%s'\n", opt); + return -1; + } + ctx->argctr++; + return 0; +} + +static int add_arg(struct fuse_opt_context *ctx, const char *arg) +{ + return fuse_opt_add_arg(&ctx->outargs, arg); +} + +int fuse_opt_add_opt(char **opts, const char *opt) +{ + char *newopts; + if (!*opts) + newopts = strdup(opt); + else { + unsigned oldlen = strlen(*opts); + newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1); + if (newopts) { + newopts[oldlen] = ','; + strcpy(newopts + oldlen + 1, opt); + } + } + if (!newopts) + return alloc_failed(); + + *opts = newopts; + return 0; +} + +static int add_opt(struct fuse_opt_context *ctx, const char *opt) +{ + return fuse_opt_add_opt(&ctx->opts, opt); +} + +static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, + int iso) +{ + if (key == FUSE_OPT_KEY_DISCARD) + return 0; + + if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { + int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); + if (res == -1 || !res) + return res; + } + if (iso) + return add_opt(ctx, arg); + else + return add_arg(ctx, arg); +} + +static int match_template(const char *t, const char *arg, unsigned *sepp) +{ + int arglen = strlen(arg); + const char *sep = strchr(t, '='); + sep = sep ? sep : strchr(t, ' '); + if (sep && (!sep[1] || sep[1] == '%')) { + int tlen = sep - t; + if (sep[0] == '=') + tlen ++; + if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { + *sepp = sep - t; + return 1; + } + } + if (strcmp(t, arg) == 0) { + *sepp = 0; + return 1; + } + return 0; +} + +static const struct fuse_opt *find_opt(const struct fuse_opt *opt, + const char *arg, unsigned *sepp) +{ + for (; opt && opt->templ; opt++) + if (match_template(opt->templ, arg, sepp)) + return opt; + return NULL; +} + +int fuse_opt_match(const struct fuse_opt *opts, const char *opt) +{ + unsigned dummy; + return find_opt(opts, opt, &dummy) ? 1 : 0; +} + +static int process_opt_param(void *var, const char *format, const char *param, + const char *arg) +{ + assert(format[0] == '%'); + if (format[1] == 's') { + char *copy = strdup(param); + if (!copy) + return alloc_failed(); + + *(char **) var = copy; + } else { + if (sscanf(param, format, var) != 1) { + fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg); + return -1; + } + } + return 0; +} + +static int process_opt(struct fuse_opt_context *ctx, + const struct fuse_opt *opt, unsigned sep, + const char *arg, int iso) +{ + if (opt->offset == -1U) { + if (call_proc(ctx, arg, opt->value, iso) == -1) + return -1; + } else { + void *var = (char *)ctx->data + opt->offset; + if (sep && opt->templ[sep + 1]) { + const char *param = arg + sep; + if (opt->templ[sep] == '=') + param ++; + if (process_opt_param(var, opt->templ + sep + 1, + param, arg) == -1) + return -1; + } else + *(int *)var = opt->value; + } + return 0; +} + +static int process_opt_sep_arg(struct fuse_opt_context *ctx, + const struct fuse_opt *opt, unsigned sep, + const char *arg, int iso) +{ + int res; + char *newarg; + char *param; + + if (next_arg(ctx, arg) == -1) + return -1; + + param = ctx->argv[ctx->argctr]; + newarg = malloc(sep + strlen(param) + 1); + if (!newarg) + return alloc_failed(); + + memcpy(newarg, arg, sep); + strcpy(newarg + sep, param); + res = process_opt(ctx, opt, sep, newarg, iso); + free(newarg); + + return res; +} + +static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) +{ + unsigned sep; + const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); + if (opt) { + for (; opt; opt = find_opt(opt + 1, arg, &sep)) { + int res; + if (sep && opt->templ[sep] == ' ' && !arg[sep]) + res = process_opt_sep_arg(ctx, opt, sep, arg, iso); + else + res = process_opt(ctx, opt, sep, arg, iso); + if (res == -1) + return -1; + } + return 0; + } else + return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); +} + +static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) +{ + char *sep; + + do { + int res; +#ifdef __SOLARIS__ + /* + * On Solaris, the device name contains commas, so the + * option "fsname" has to be the last one and its commas + * should not be interpreted as option separators. + * This had to be hardcoded because the option "fsname" + * may be found though not present in option list. + */ + if (!strncmp(opts,"fsname=",7)) + sep = (char*)NULL; + else +#endif /* __SOLARIS__ */ + { + sep = strchr(opts, ','); + if (sep) + *sep = '\0'; + } + res = process_gopt(ctx, opts, 1); + if (res == -1) + return -1; + opts = sep + 1; + } while (sep); + + return 0; +} + +static int process_option_group(struct fuse_opt_context *ctx, const char *opts) +{ + int res; + char *copy; + const char *sep = strchr(opts, ','); + if (!sep) + return process_gopt(ctx, opts, 1); + + copy = strdup(opts); + if (!copy) { + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; + } + res = process_real_option_group(ctx, copy); + free(copy); + return res; +} + +static int process_one(struct fuse_opt_context *ctx, const char *arg) +{ + if (ctx->nonopt || arg[0] != '-') + return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); + else if (arg[1] == 'o') { + if (arg[2]) + return process_option_group(ctx, arg + 2); + else { + if (next_arg(ctx, arg) == -1) + return -1; + + return process_option_group(ctx, ctx->argv[ctx->argctr]); + } + } else if (arg[1] == '-' && !arg[2]) { + if (add_arg(ctx, arg) == -1) + return -1; + ctx->nonopt = ctx->outargs.argc; + return 0; + } else + return process_gopt(ctx, arg, 0); +} + +static int opt_parse(struct fuse_opt_context *ctx) +{ + if (ctx->argc) { + if (add_arg(ctx, ctx->argv[0]) == -1) + return -1; + } + + for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) + if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) + return -1; + + if (ctx->opts) { + if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || + fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) + return -1; + } + if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc) { + free(ctx->outargs.argv[ctx->outargs.argc - 1]); + ctx->outargs.argv[--ctx->outargs.argc] = NULL; + } + + return 0; +} + +int fuse_opt_parse(struct fuse_args *args, void *data, + const struct fuse_opt opts[], fuse_opt_proc_t proc) +{ + int res; + struct fuse_opt_context ctx = { + .data = data, + .opt = opts, + .proc = proc, + }; + + if (!args || !args->argv || !args->argc) + return 0; + + ctx.argc = args->argc; + ctx.argv = args->argv; + + res = opt_parse(&ctx); + if (res != -1) { + struct fuse_args tmp = *args; + *args = ctx.outargs; + ctx.outargs = tmp; + } + free(ctx.opts); + fuse_opt_free_args(&ctx.outargs); + return res; +} diff --git a/libfuse-lite/fuse_session.c b/libfuse-lite/fuse_session.c new file mode 100755 index 0000000000000000000000000000000000000000..3773303a55ff379aa5dc131bdf52e68e519e4d20 --- /dev/null +++ b/libfuse-lite/fuse_session.c @@ -0,0 +1,193 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "config.h" +#include "fuse_lowlevel.h" +#include "fuse_lowlevel_compat.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +struct fuse_session { + struct fuse_session_ops op; + + void *data; + + volatile int exited; + + struct fuse_chan *ch; +}; + +struct fuse_chan { + struct fuse_chan_ops op; + + struct fuse_session *se; + + int fd; + + size_t bufsize; + + void *data; +}; + +struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data) +{ + struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se)); + if (se == NULL) { + fprintf(stderr, "fuse: failed to allocate session\n"); + return NULL; + } + + memset(se, 0, sizeof(*se)); + se->op = *op; + se->data = data; + + return se; +} + +void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch) +{ + assert(se->ch == NULL); + assert(ch->se == NULL); + se->ch = ch; + ch->se = se; +} + +void fuse_session_remove_chan(struct fuse_chan *ch) +{ + struct fuse_session *se = ch->se; + if (se) { + assert(se->ch == ch); + se->ch = NULL; + ch->se = NULL; + } +} + +struct fuse_chan *fuse_session_next_chan(struct fuse_session *se, + struct fuse_chan *ch) +{ + assert(ch == NULL || ch == se->ch); + if (ch == NULL) + return se->ch; + else + return NULL; +} + +void fuse_session_process(struct fuse_session *se, const char *buf, size_t len, + struct fuse_chan *ch) +{ + se->op.process(se->data, buf, len, ch); +} + +void fuse_session_destroy(struct fuse_session *se) +{ + if (se->op.destroy) + se->op.destroy(se->data); + if (se->ch != NULL) + fuse_chan_destroy(se->ch); + free(se); +} + +void fuse_session_exit(struct fuse_session *se) +{ + if (se->op.exit) + se->op.exit(se->data, 1); + se->exited = 1; +} + +void fuse_session_reset(struct fuse_session *se) +{ + if (se->op.exit) + se->op.exit(se->data, 0); + se->exited = 0; +} + +int fuse_session_exited(struct fuse_session *se) +{ + if (se->op.exited) + return se->op.exited(se->data); + else + return se->exited; +} + +static struct fuse_chan *fuse_chan_new_common(struct fuse_chan_ops *op, int fd, + size_t bufsize, void *data) +{ + struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch)); + if (ch == NULL) { + fprintf(stderr, "fuse: failed to allocate channel\n"); + return NULL; + } + + memset(ch, 0, sizeof(*ch)); + ch->op = *op; + ch->fd = fd; + ch->bufsize = bufsize; + ch->data = data; + + return ch; +} + +struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd, + size_t bufsize, void *data) +{ + return fuse_chan_new_common(op, fd, bufsize, data); +} + +int fuse_chan_fd(struct fuse_chan *ch) +{ + return ch->fd; +} + +size_t fuse_chan_bufsize(struct fuse_chan *ch) +{ + return ch->bufsize; +} + +void *fuse_chan_data(struct fuse_chan *ch) +{ + return ch->data; +} + +struct fuse_session *fuse_chan_session(struct fuse_chan *ch) +{ + return ch->se; +} + +int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size) +{ + struct fuse_chan *ch = *chp; + + return ch->op.receive(chp, buf, size); +} + +#ifdef __SOLARIS__ +int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size) +{ + int res; + + res = fuse_chan_recv(&ch, buf, size); + return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0; +} +#endif /* __SOLARIS__ */ + +int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count) +{ + return ch->op.send(ch, iov, count); +} + +void fuse_chan_destroy(struct fuse_chan *ch) +{ + fuse_session_remove_chan(ch); + if (ch->op.destroy) + ch->op.destroy(ch); + free(ch); +} diff --git a/libfuse-lite/fuse_signals.c b/libfuse-lite/fuse_signals.c new file mode 100755 index 0000000000000000000000000000000000000000..bf979563268b7dde9d2acaae597697718d0fd674 --- /dev/null +++ b/libfuse-lite/fuse_signals.c @@ -0,0 +1,73 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB +*/ + +#include "config.h" +#include "fuse_lowlevel.h" + +#include <stdio.h> +#include <string.h> +#include <signal.h> + +static struct fuse_session *fuse_instance; + +static void exit_handler(int sig) +{ + (void) sig; + if (fuse_instance) + fuse_session_exit(fuse_instance); +} + +static int set_one_signal_handler(int sig, void (*handler)(int)) +{ + struct sigaction sa; + struct sigaction old_sa; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = handler; + sigemptyset(&(sa.sa_mask)); + sa.sa_flags = 0; + + if (sigaction(sig, NULL, &old_sa) == -1) { + perror("fuse: cannot get old signal handler"); + return -1; + } + + if (old_sa.sa_handler == SIG_DFL && + sigaction(sig, &sa, NULL) == -1) { + perror("fuse: cannot set signal handler"); + return -1; + } + return 0; +} + +int fuse_set_signal_handlers(struct fuse_session *se) +{ + if (set_one_signal_handler(SIGHUP, exit_handler) == -1 || + set_one_signal_handler(SIGINT, exit_handler) == -1 || + set_one_signal_handler(SIGTERM, exit_handler) == -1 || + set_one_signal_handler(SIGPIPE, SIG_IGN) == -1) + return -1; + + fuse_instance = se; + return 0; +} + +void fuse_remove_signal_handlers(struct fuse_session *se) +{ + if (fuse_instance != se) + fprintf(stderr, + "fuse: fuse_remove_signal_handlers: unknown session\n"); + else + fuse_instance = NULL; + + set_one_signal_handler(SIGHUP, SIG_DFL); + set_one_signal_handler(SIGINT, SIG_DFL); + set_one_signal_handler(SIGTERM, SIG_DFL); + set_one_signal_handler(SIGPIPE, SIG_DFL); +} + diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c new file mode 100755 index 0000000000000000000000000000000000000000..c94d5838bec2c5c2ffc3ba674d0c5745182db430 --- /dev/null +++ b/libfuse-lite/fusermount.c @@ -0,0 +1,709 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. +*/ +/* This program does the mounting and unmounting of FUSE filesystems */ + +#include <config.h> + +#include "mount_util.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <getopt.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> + +#ifdef __SOLARIS__ +#include <sys/mnttab.h> +#else /* __SOLARIS__ */ +#include <grp.h> +#include <mntent.h> +#include <sys/fsuid.h> +#endif /* __SOLARIS__ */ + +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/socket.h> +#include <sys/utsname.h> + +#define FUSE_DEV_NEW "/dev/fuse" + +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 +#endif + +static const char *progname = "ntfs-3g-mount"; + +static int mount_max = 1000; + +int drop_privs(void); +int restore_privs(void); + +#ifdef __SOLARIS__ + +/* + * fusermount is not implemented in fuse-lite for Solaris, + * only the minimal functions are provided. + */ + +/* + * Solaris doesn't have setfsuid/setfsgid. + * This doesn't really matter anyway as this program shouldn't be made + * suid on Solaris. It should instead be used via a profile with the + * sys_mount privilege. + */ + +int drop_privs(void) +{ + return (0); +} + +int restore_privs(void) +{ + return (0); +} + +#else /* __SOLARIS__ */ + +static const char *get_user_name(void) +{ + struct passwd *pw = getpwuid(getuid()); + if (pw != NULL && pw->pw_name != NULL) + return pw->pw_name; + else { + fprintf(stderr, "%s: could not determine username\n", progname); + return NULL; + } +} + +int drop_privs(void) +{ + if (!getegid()) { + + gid_t new_gid = getgid(); + + if (setresgid(-1, new_gid, getegid()) < 0) { + perror("priv drop: setresgid failed"); + return -1; + } + if (getegid() != new_gid){ + perror("dropping group privilege failed"); + return -1; + } + } + + if (!geteuid()) { + + uid_t new_uid = getuid(); + + if (setresuid(-1, new_uid, geteuid()) < 0) { + perror("priv drop: setresuid failed"); + return -1; + } + if (geteuid() != new_uid){ + perror("dropping user privilege failed"); + return -1; + } + } + + return 0; +} + +int restore_privs(void) +{ + if (geteuid()) { + + uid_t ruid, euid, suid; + + if (getresuid(&ruid, &euid, &suid) < 0) { + perror("priv restore: getresuid failed"); + return -1; + } + if (setresuid(-1, suid, -1) < 0) { + perror("priv restore: setresuid failed"); + return -1; + } + if (geteuid() != suid) { + perror("restoring privilege failed"); + return -1; + } + } + + if (getegid()) { + + gid_t rgid, egid, sgid; + + if (getresgid(&rgid, &egid, &sgid) < 0) { + perror("priv restore: getresgid failed"); + return -1; + } + if (setresgid(-1, sgid, -1) < 0) { + perror("priv restore: setresgid failed"); + return -1; + } + if (getegid() != sgid){ + perror("restoring group privilege failed"); + return -1; + } + } + + return 0; +} + +#ifndef IGNORE_MTAB +static int add_mount(const char *source, const char *mnt, const char *type, + const char *opts) +{ + return fuse_mnt_add_mount(progname, source, mnt, type, opts); +} + +static int count_fuse_fs(void) +{ + struct mntent *entp; + int count = 0; + const char *mtab = _PATH_MOUNTED; + FILE *fp = setmntent(mtab, "r"); + if (fp == NULL) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab, + strerror(errno)); + return -1; + } + while ((entp = getmntent(fp)) != NULL) { + if (strcmp(entp->mnt_type, "fuse") == 0 || + strncmp(entp->mnt_type, "fuse.", 5) == 0) + count ++; + } + endmntent(fp); + return count; +} + + +#else /* IGNORE_MTAB */ +static int count_fuse_fs() +{ + return 0; +} + +static int add_mount(const char *source, const char *mnt, const char *type, + const char *opts) +{ + (void) source; + (void) mnt; + (void) type; + (void) opts; + return 0; +} +#endif /* IGNORE_MTAB */ + +static int begins_with(const char *s, const char *beg) +{ + if (strncmp(s, beg, strlen(beg)) == 0) + return 1; + else + return 0; +} + +struct mount_flags { + const char *opt; + unsigned long flag; + int on; + int safe; +}; + +static struct mount_flags mount_flags[] = { + {"rw", MS_RDONLY, 0, 1}, + {"ro", MS_RDONLY, 1, 1}, + {"suid", MS_NOSUID, 0, 0}, + {"nosuid", MS_NOSUID, 1, 1}, + {"dev", MS_NODEV, 0, 0}, + {"nodev", MS_NODEV, 1, 1}, + {"exec", MS_NOEXEC, 0, 1}, + {"noexec", MS_NOEXEC, 1, 1}, + {"async", MS_SYNCHRONOUS, 0, 1}, + {"sync", MS_SYNCHRONOUS, 1, 1}, + {"atime", MS_NOATIME, 0, 1}, + {"noatime", MS_NOATIME, 1, 1}, + {"dirsync", MS_DIRSYNC, 1, 1}, + {NULL, 0, 0, 0} +}; + +static int find_mount_flag(const char *s, unsigned len, int *on, int *flag) +{ + int i; + + for (i = 0; mount_flags[i].opt != NULL; i++) { + const char *opt = mount_flags[i].opt; + if (strlen(opt) == len && strncmp(opt, s, len) == 0) { + *on = mount_flags[i].on; + *flag = mount_flags[i].flag; + if (!mount_flags[i].safe && getuid() != 0) { + *flag = 0; + fprintf(stderr, "%s: unsafe option '%s' ignored\n", + progname, opt); + } + return 1; + } + } + return 0; +} + +static int add_option(char **optsp, const char *opt, unsigned expand) +{ + char *newopts; + if (*optsp == NULL) + newopts = strdup(opt); + else { + unsigned oldsize = strlen(*optsp); + unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1; + newopts = (char *) realloc(*optsp, newsize); + if (newopts) + sprintf(newopts + oldsize, ",%s", opt); + } + if (newopts == NULL) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return -1; + } + *optsp = newopts; + return 0; +} + +static int get_mnt_opts(int flags, char *opts, char **mnt_optsp) +{ + int i; + int l; + + if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1) + return -1; + + for (i = 0; mount_flags[i].opt != NULL; i++) { + if (mount_flags[i].on && (flags & mount_flags[i].flag) && + add_option(mnt_optsp, mount_flags[i].opt, 0) == -1) + return -1; + } + + if (add_option(mnt_optsp, opts, 0) == -1) + return -1; + /* remove comma from end of opts*/ + l = strlen(*mnt_optsp); + if ((*mnt_optsp)[l-1] == ',') + (*mnt_optsp)[l-1] = '\0'; + if (getuid() != 0) { + const char *user = get_user_name(); + if (user == NULL) + return -1; + + if (add_option(mnt_optsp, "user=", strlen(user)) == -1) + return -1; + strcat(*mnt_optsp, user); + } + return 0; +} + +static int opt_eq(const char *s, unsigned len, const char *opt) +{ + if(strlen(opt) == len && strncmp(s, opt, len) == 0) + return 1; + else + return 0; +} + +static int get_string_opt(const char *s, unsigned len, const char *opt, + char **val) +{ + unsigned opt_len = strlen(opt); + + if (*val) + free(*val); + *val = (char *) malloc(len - opt_len + 1); + if (!*val) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return 0; + } + + memcpy(*val, s + opt_len, len - opt_len); + (*val)[len - opt_len] = '\0'; + return 1; +} + +static int do_mount(const char *mnt, char **typep, mode_t rootmode, + int fd, const char *opts, const char *dev, char **sourcep, + char **mnt_optsp) +{ + int res; + int flags = MS_NOSUID | MS_NODEV; + char *optbuf; + char *mnt_opts = NULL; + const char *s; + char *d; + char *fsname = NULL; + char *source = NULL; + char *type = NULL; + int blkdev = 0; + + optbuf = (char *) malloc(strlen(opts) + 128); + if (!optbuf) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return -1; + } + + for (s = opts, d = optbuf; *s;) { + unsigned len; + const char *fsname_str = "fsname="; + for (len = 0; s[len] && s[len] != ','; len++); + if (begins_with(s, fsname_str)) { + if (!get_string_opt(s, len, fsname_str, &fsname)) + goto err; + } else if (opt_eq(s, len, "blkdev")) { + blkdev = 1; + } else if (!begins_with(s, "fd=") && + !begins_with(s, "rootmode=") && + !begins_with(s, "user_id=") && + !begins_with(s, "group_id=")) { + int on; + int flag; + int skip_option = 0; + if (opt_eq(s, len, "large_read")) { + struct utsname utsname; + unsigned kmaj, kmin; + res = uname(&utsname); + if (res == 0 && + sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 && + (kmaj > 2 || (kmaj == 2 && kmin > 4))) { + fprintf(stderr, "%s: note: 'large_read' mount option is " + "deprecated for %i.%i kernels\n", progname, kmaj, kmin); + skip_option = 1; + } + } + if (!skip_option) { + if (find_mount_flag(s, len, &on, &flag)) { + if (on) + flags |= flag; + else + flags &= ~flag; + } else { + memcpy(d, s, len); + d += len; + *d++ = ','; + } + } + } + s += len; + if (*s) + s++; + } + *d = '\0'; + res = get_mnt_opts(flags, optbuf, &mnt_opts); + if (res == -1) + goto err; + + sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i", + fd, rootmode, getuid(), getgid()); + + source = malloc((fsname ? strlen(fsname) : 0) + strlen(dev) + 32); + + type = malloc(32); + if (!type || !source) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + goto err; + } + + strcpy(type, blkdev ? "fuseblk" : "fuse"); + + if (fsname) + strcpy(source, fsname); + else + strcpy(source, dev); + + char * rchr = strchr(source, '@'); + if (rchr) { + rchr[0] = ','; + } + + if (restore_privs()) + goto err; + + res = mount(source, mnt, type, flags, optbuf); + if (res == -1 && errno == EINVAL) { + /* It could be an old version not supporting group_id */ + sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid()); + res = mount(source, mnt, type, flags, optbuf); + } + + if (drop_privs()) + goto err; + + if (res == -1) { + int errno_save = errno; + if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk()) + fprintf(stderr, "%s: 'fuseblk' support missing\n", progname); + else { + fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save)); + if (errno_save == EPERM) + fprintf(stderr, "User doesn't have privilege to mount. " + "For more information\nplease see: " + "http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"); + } + goto err; + } else { + *sourcep = source; + *typep = type; + *mnt_optsp = mnt_opts; + } +out: + free(fsname); + free(optbuf); + return res; +err: + free(source); + free(type); + free(mnt_opts); + res = -1; + goto out; +} + +static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd, + int *mountpoint_fd) +{ + int res; + const char *mnt = *mntp; + const char *origmnt = mnt; + + res = stat(mnt, stbuf); + if (res == -1) { + fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", + progname, mnt, strerror(errno)); + return -1; + } + + /* No permission checking is done for root */ + if (getuid() == 0) + return 0; + + if (S_ISDIR(stbuf->st_mode)) { + *currdir_fd = open(".", O_RDONLY); + if (*currdir_fd == -1) { + fprintf(stderr, "%s: failed to open current directory: %s\n", + progname, strerror(errno)); + return -1; + } + res = chdir(mnt); + if (res == -1) { + fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n", + progname, strerror(errno)); + return -1; + } + mnt = *mntp = "."; + res = lstat(mnt, stbuf); + if (res == -1) { + fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", + progname, origmnt, strerror(errno)); + return -1; + } + + if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) { + fprintf(stderr, "%s: mountpoint %s not owned by user\n", + progname, origmnt); + return -1; + } + + res = access(mnt, W_OK); + if (res == -1) { + fprintf(stderr, "%s: user has no write access to mountpoint %s\n", + progname, origmnt); + return -1; + } + } else if (S_ISREG(stbuf->st_mode)) { + static char procfile[256]; + *mountpoint_fd = open(mnt, O_WRONLY); + if (*mountpoint_fd == -1) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, mnt, + strerror(errno)); + return -1; + } + res = fstat(*mountpoint_fd, stbuf); + if (res == -1) { + fprintf(stderr, "%s: failed to access mountpoint %s: %s\n", + progname, mnt, strerror(errno)); + return -1; + } + if (!S_ISREG(stbuf->st_mode)) { + fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n", + progname, mnt); + return -1; + } + + sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd); + *mntp = procfile; + } else { + fprintf(stderr, + "%s: mountpoint %s is not a directory or a regular file\n", + progname, mnt); + return -1; + } + + + return 0; +} + +static int try_open(const char *dev, char **devp) +{ + int fd; + + if (restore_privs()) + return -1; + fd = open(dev, O_RDWR); + if (drop_privs()) + return -1; + if (fd != -1) { + *devp = strdup(dev); + if (*devp == NULL) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + close(fd); + fd = -1; + } + } else if (errno == ENODEV || + errno == ENOENT) /* check for ENOENT too, for the udev case */ + return -2; + else { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev, + strerror(errno)); + } + return fd; +} + +static int open_fuse_device(char **devp) +{ + int fd; + + fd = try_open(FUSE_DEV_NEW, devp); + if (fd >= -1) + return fd; + + fprintf(stderr, "%s: fuse device is missing, try 'modprobe fuse' as root\n", + progname); + + return -1; +} + + +static int mount_fuse(const char *mnt, const char *opts) +{ + int res; + int fd; + char *dev; + struct stat stbuf; + char *type = NULL; + char *source = NULL; + char *mnt_opts = NULL; + const char *real_mnt = mnt; + int currdir_fd = -1; + int mountpoint_fd = -1; + + fd = open_fuse_device(&dev); + if (fd == -1) + return -1; + + if (getuid() != 0 && mount_max != -1) { + if (count_fuse_fs() >= mount_max) { + fprintf(stderr, "%s: too many mounted FUSE filesystems (%d+)\n", + progname, mount_max); + goto err; + } + } + + res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd); + if (res != -1) + res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts, dev, + &source, &mnt_opts); + + if (currdir_fd != -1) { + __attribute__((unused))int ignored_fchdir_status = + fchdir(currdir_fd); + close(currdir_fd); + } + if (mountpoint_fd != -1) + close(mountpoint_fd); + + if (res == -1) + goto err; + + if (restore_privs()) + goto err; + + if (geteuid() == 0) { + + if (setgroups(0, NULL) == -1) { + perror("priv drop: setgroups failed"); + goto err; + } + + res = add_mount(source, mnt, type, mnt_opts); + if (res == -1) { + umount2(mnt, 2); /* lazy umount */ + drop_privs(); + goto err; + } + } + + if (drop_privs()) + goto err; +out: + free(source); + free(type); + free(mnt_opts); + free(dev); + + return fd; +err: + close(fd); + fd = -1; + goto out; +} + +int fusermount(int unmount, int quiet, int lazy, const char *opts, + const char *origmnt) +{ + int res = -1; + char *mnt; + mode_t old_umask; + + mnt = fuse_mnt_resolve_path(progname, origmnt); + if (mnt == NULL) + return -1; + + old_umask = umask(033); + + if (unmount) { + + if (restore_privs()) + goto out; + + if (geteuid() == 0) + res = fuse_mnt_umount(progname, mnt, lazy); + else { + res = umount2(mnt, lazy ? 2 : 0); + if (res == -1 && !quiet) + fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, + mnt, strerror(errno)); + } + + if (drop_privs()) + res = -1; + + } else + res = mount_fuse(mnt, opts); +out: + umask(old_umask); + free(mnt); + return res; +} + +#endif /* __SOLARIS__ */ diff --git a/libfuse-lite/helper.c b/libfuse-lite/helper.c new file mode 100755 index 0000000000000000000000000000000000000000..ebd7d77ec6b2778bfeca2af2eb49094af4495123 --- /dev/null +++ b/libfuse-lite/helper.c @@ -0,0 +1,61 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include "config.h" +#include "fuse_i.h" +#include "fuse_lowlevel.h" + +struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args) +{ + struct fuse_chan *ch; + int fd; + +#ifdef __SOLARIS__ + /* + * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos + * would ensue. + */ + do { + fd = open("/dev/null", O_RDWR); + if (fd > 2) + close(fd); + } while (fd >= 0 && fd <= 2); +#endif /* __SOLARIS__ */ + + fd = fuse_kern_mount(mountpoint, args); + if (fd == -1) + return NULL; + + ch = fuse_kern_chan_new(fd); + if (!ch) + fuse_kern_unmount(mountpoint, fd); + + return ch; +} + +void fuse_unmount(const char *mountpoint, struct fuse_chan *ch) +{ + int fd = ch ? fuse_chan_fd(ch) : -1; + fuse_kern_unmount(mountpoint, fd); + fuse_chan_destroy(ch); +} + +int fuse_version(void) +{ + return FUSE_VERSION; +} + +#ifdef __SOLARIS__ +#undef fuse_main +int fuse_main(void); +int fuse_main(void) +{ + fprintf(stderr, "fuse_main(): This function does not exist\n"); + return -1; +} +#endif /* __SOLARIS__ */ diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c new file mode 100755 index 0000000000000000000000000000000000000000..70454f4e3783555e4c713582e6275a53ed238f2f --- /dev/null +++ b/libfuse-lite/mount.c @@ -0,0 +1,732 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include "config.h" +#include "fuse_i.h" +#include "fuse_opt.h" +#include "mount_util.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/wait.h> +#include <sys/mount.h> + +#ifdef __SOLARIS__ + +#define FUSERMOUNT_PROG "fusermount" +#define FUSE_COMMFD_ENV "_FUSE_COMMFD" + +#ifndef FUSERMOUNT_DIR +#define FUSERMOUNT_DIR "/usr" +#endif /* FUSERMOUNT_DIR */ + +#ifndef HAVE_FORK +#define fork() vfork() +#endif + +#endif /* __SOLARIS__ */ + +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 +#endif + +enum { + KEY_KERN_FLAG, + KEY_KERN_OPT, + KEY_FUSERMOUNT_OPT, + KEY_SUBTYPE_OPT, + KEY_MTAB_OPT, + KEY_ALLOW_ROOT, + KEY_RO, + KEY_HELP, + KEY_VERSION, +}; + +struct mount_opts { + int allow_other; + int allow_root; + int ishelp; + int flags; +#ifdef __SOLARIS__ + int nonempty; + int blkdev; + char *fsname; + char *subtype; + char *subtype_opt; +#else + int blkdev; + char *fsname; +#endif + char *mtab_opts; + char *fusermount_opts; + char *kernel_opts; +}; + +#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 } + +static const struct fuse_opt fuse_mount_opts[] = { +#ifdef __SOLARIS__ + FUSE_MOUNT_OPT("allow_other", allow_other), + FUSE_MOUNT_OPT("allow_root", allow_root), + FUSE_MOUNT_OPT("nonempty", nonempty), + FUSE_MOUNT_OPT("blkdev", blkdev), + FUSE_MOUNT_OPT("fsname=%s", fsname), + FUSE_MOUNT_OPT("subtype=%s", subtype), + FUSE_OPT_KEY("allow_other", KEY_KERN_OPT), + FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT), + FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT), + FUSE_OPT_KEY("large_read", KEY_KERN_OPT), + FUSE_OPT_KEY("blksize=", KEY_KERN_OPT), + FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT), + FUSE_OPT_KEY("max_read=", KEY_KERN_OPT), + FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("user=", KEY_MTAB_OPT), + FUSE_OPT_KEY("-r", KEY_RO), + FUSE_OPT_KEY("ro", KEY_KERN_FLAG), + FUSE_OPT_KEY("rw", KEY_KERN_FLAG), + FUSE_OPT_KEY("suid", KEY_KERN_FLAG), + FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG), + FUSE_OPT_KEY("-g", KEY_KERN_FLAG), + FUSE_OPT_KEY("-m", KEY_KERN_FLAG), + FUSE_OPT_KEY("-O", KEY_KERN_FLAG), + FUSE_OPT_KEY("setuid", KEY_KERN_OPT), + FUSE_OPT_KEY("nosetuid", KEY_KERN_OPT), + FUSE_OPT_KEY("devices", KEY_KERN_OPT), + FUSE_OPT_KEY("nodevices", KEY_KERN_OPT), + FUSE_OPT_KEY("exec", KEY_KERN_OPT), + FUSE_OPT_KEY("noexec", KEY_KERN_OPT), + FUSE_OPT_KEY("nbmand", KEY_KERN_OPT), + FUSE_OPT_KEY("nonbmand", KEY_KERN_OPT), +#else /* __SOLARIS__ */ + FUSE_MOUNT_OPT("allow_other", allow_other), + FUSE_MOUNT_OPT("allow_root", allow_root), + FUSE_MOUNT_OPT("blkdev", blkdev), + FUSE_MOUNT_OPT("fsname=%s", fsname), + FUSE_OPT_KEY("allow_other", KEY_KERN_OPT), + FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT), + FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("large_read", KEY_KERN_OPT), + FUSE_OPT_KEY("blksize=", KEY_KERN_OPT), + FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT), + FUSE_OPT_KEY("context=", KEY_KERN_OPT), + FUSE_OPT_KEY("max_read=", KEY_KERN_OPT), + FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("user=", KEY_MTAB_OPT), + FUSE_OPT_KEY("-r", KEY_RO), + FUSE_OPT_KEY("ro", KEY_KERN_FLAG), + FUSE_OPT_KEY("rw", KEY_KERN_FLAG), + FUSE_OPT_KEY("suid", KEY_KERN_FLAG), + FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG), + FUSE_OPT_KEY("dev", KEY_KERN_FLAG), + FUSE_OPT_KEY("nodev", KEY_KERN_FLAG), + FUSE_OPT_KEY("exec", KEY_KERN_FLAG), + FUSE_OPT_KEY("noexec", KEY_KERN_FLAG), + FUSE_OPT_KEY("async", KEY_KERN_FLAG), + FUSE_OPT_KEY("sync", KEY_KERN_FLAG), + FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG), + FUSE_OPT_KEY("atime", KEY_KERN_FLAG), + FUSE_OPT_KEY("noatime", KEY_KERN_FLAG), +#endif /* __SOLARIS__ */ + FUSE_OPT_KEY("-h", KEY_HELP), + FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_OPT_KEY("-V", KEY_VERSION), + FUSE_OPT_KEY("--version", KEY_VERSION), + FUSE_OPT_END +}; + +#ifdef __SOLARIS__ + +static void mount_help(void) +{ + fprintf(stderr, + " -o allow_other allow access to other users\n" + " -o allow_root allow access to root\n" + " -o nonempty allow mounts over non-empty file/dir\n" + " -o default_permissions enable permission checking by kernel\n" + " -o fsname=NAME set filesystem name\n" + " -o subtype=NAME set filesystem type\n" + " -o large_read issue large read requests (2.4 only)\n" + " -o max_read=N set maximum size of read requests\n" + "\n" + ); +} + +static void exec_fusermount(const char *argv[]) +{ + execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv); + execvp(FUSERMOUNT_PROG, (char **) argv); +} + +static void mount_version(void) +{ + int pid = fork(); + if (!pid) { + const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL }; + exec_fusermount(argv); + _exit(1); + } else if (pid != -1) + waitpid(pid, NULL, 0); +} + +#endif /* __SOLARIS__ */ + +struct mount_flags { + const char *opt; + unsigned long flag; + int on; +}; + +static struct mount_flags mount_flags[] = { + {"rw", MS_RDONLY, 0}, + {"ro", MS_RDONLY, 1}, + {"suid", MS_NOSUID, 0}, + {"nosuid", MS_NOSUID, 1}, +#ifndef __SOLARIS__ + {"dev", MS_NODEV, 0}, + {"nodev", MS_NODEV, 1}, + {"exec", MS_NOEXEC, 0}, + {"noexec", MS_NOEXEC, 1}, + {"async", MS_SYNCHRONOUS, 0}, + {"sync", MS_SYNCHRONOUS, 1}, + {"atime", MS_NOATIME, 0}, + {"noatime", MS_NOATIME, 1}, + {"dirsync", MS_DIRSYNC, 1}, +#else /* __SOLARIS__ */ + {"-g", MS_GLOBAL, 1}, /* 1eaf4 */ + {"-m", MS_NOMNTTAB, 1}, /* 1eb00 */ + {"-O", MS_OVERLAY, 1}, /* 1eb0c */ +#endif /* __SOLARIS__ */ + {NULL, 0, 0} +}; + +#ifdef __SOLARIS__ + +/* + * See comments in fuse_kern_mount() + */ +struct solaris_mount_opts { + int nosuid; + int setuid; + int nosetuid; + int devices; + int nodevices; +}; + +#define SOLARIS_MOUNT_OPT(t, p, n) \ + { t, offsetof(struct solaris_mount_opts, p), n } +static const struct fuse_opt solaris_mnt_opts[] = { + SOLARIS_MOUNT_OPT("suid", setuid, 1), + SOLARIS_MOUNT_OPT("suid", devices, 1), + SOLARIS_MOUNT_OPT("nosuid", nosuid, 1), + SOLARIS_MOUNT_OPT("setuid", setuid, 1), + SOLARIS_MOUNT_OPT("nosetuid", nosetuid, 1), + SOLARIS_MOUNT_OPT("devices", devices, 1), + SOLARIS_MOUNT_OPT("nodevices", nodevices, 1), + FUSE_OPT_END +}; + +#endif /* __SOLARIS__ */ + +static void set_mount_flag(const char *s, int *flags) +{ + int i; + + for (i = 0; mount_flags[i].opt != NULL; i++) { + const char *opt = mount_flags[i].opt; + if (strcmp(opt, s) == 0) { + if (mount_flags[i].on) + *flags |= mount_flags[i].flag; + else + *flags &= ~mount_flags[i].flag; + return; + } + } + fprintf(stderr, "fuse: internal error, can't find mount flag\n"); + abort(); +} + +static int fuse_mount_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + struct mount_opts *mo = data; + + switch (key) { + case KEY_ALLOW_ROOT: + if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 || + fuse_opt_add_arg(outargs, "-oallow_root") == -1) + return -1; + return 0; + + case KEY_RO: + arg = "ro"; + /* fall through */ + case KEY_KERN_FLAG: + set_mount_flag(arg, &mo->flags); + return 0; + + case KEY_KERN_OPT: + return fuse_opt_add_opt(&mo->kernel_opts, arg); + + case KEY_FUSERMOUNT_OPT: + return fuse_opt_add_opt(&mo->fusermount_opts, arg); + +#ifdef __SOLARIS__ + case KEY_SUBTYPE_OPT: + return fuse_opt_add_opt(&mo->subtype_opt, arg); +#endif /* __SOLARIS__ */ + + case KEY_MTAB_OPT: + return fuse_opt_add_opt(&mo->mtab_opts, arg); + + case KEY_HELP: +#ifdef __SOLARIS__ + mount_help(); +#endif /* __SOLARIS__ */ + mo->ishelp = 1; + break; + + case KEY_VERSION: +#ifdef __SOLARIS__ + mount_version(); +#endif /* __SOLARIS__ */ + mo->ishelp = 1; + break; + } + return 1; +} + +#ifdef __SOLARIS__ + +/* return value: + * >= 0 => fd + * -1 => error + */ +static int receive_fd(int fd) +{ + struct msghdr msg; + struct iovec iov; + char buf[1]; + int rv; + size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)]; + struct cmsghdr *cmsg; + + iov.iov_base = buf; + iov.iov_len = 1; + + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + /* old BSD implementations should use msg_accrights instead of + * msg_control; the interface is different. */ + msg.msg_control = ccmsg; + msg.msg_controllen = sizeof(ccmsg); + + while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR); + if (rv == -1) { + perror("recvmsg"); + return -1; + } + if(!rv) { + /* EOF */ + return -1; + } + + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg->cmsg_type == SCM_RIGHTS) { + fprintf(stderr, "got control message of unknown type %d\n", + cmsg->cmsg_type); + return -1; + } + return *(int*)CMSG_DATA(cmsg); +} + +#endif /* __SOLARIS__ */ + +void fuse_kern_unmount(const char *mountpoint, int fd) +{ + int res; +#ifdef __SOLARIS__ + int pid; +#endif /* __SOLARIS__ */ + + if (!mountpoint) + return; + + if (fd != -1) { + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = 0; + res = poll(&pfd, 1, 0); + /* If file poll returns POLLERR on the device file descriptor, + then the filesystem is already unmounted */ + if (res == 1 && (pfd.revents & POLLERR)) + return; + } +#ifndef __SOLARIS__ + close(fd); + + fusermount(1, 0, 1, "", mountpoint); +#else /* __SOLARIS__ */ + if (geteuid() == 0) { + fuse_mnt_umount("fuse", mountpoint, 1); + return; + } + + res = umount2(mountpoint, 2); + if (res == 0) + return; + + pid = fork(); + if(pid == -1) + return; + + if(pid == 0) { + const char *argv[] = + { FUSERMOUNT_PROG, "-u", "-q", "-z", "--", mountpoint, NULL }; + + exec_fusermount(argv); + _exit(1); + } + waitpid(pid, NULL, 0); +#endif /* __SOLARIS__ */ +} + +#ifdef __SOLARIS__ + +static int fuse_mount_fusermount(const char *mountpoint, const char *opts, + int quiet) +{ + int fds[2], pid; + int res; + int rv; + + if (!mountpoint) { + fprintf(stderr, "fuse: missing mountpoint\n"); + return -1; + } + + res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); + if(res == -1) { + perror("fuse: socketpair() failed"); + return -1; + } + + pid = fork(); + if(pid == -1) { + perror("fuse: fork() failed"); + close(fds[0]); + close(fds[1]); + return -1; + } + + if(pid == 0) { + char env[10]; + const char *argv[32]; + int a = 0; + + if (quiet) { + int fd = open("/dev/null", O_RDONLY); + dup2(fd, 1); + dup2(fd, 2); + } + + argv[a++] = FUSERMOUNT_PROG; + if (opts) { + argv[a++] = "-o"; + argv[a++] = opts; + } + argv[a++] = "--"; + argv[a++] = mountpoint; + argv[a++] = NULL; + + close(fds[1]); + fcntl(fds[0], F_SETFD, 0); + snprintf(env, sizeof(env), "%i", fds[0]); + setenv(FUSE_COMMFD_ENV, env, 1); + exec_fusermount(argv); + perror("fuse: failed to exec fusermount"); + _exit(1); + } + + close(fds[0]); + rv = receive_fd(fds[1]); + close(fds[1]); + waitpid(pid, NULL, 0); /* bury zombie */ + + return rv; +} + +static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, + const char *mnt_opts) +{ + char tmp[128]; + const char *devname = "/dev/fuse"; + char *source = NULL; + char *type = NULL; + struct stat stbuf; + int fd; + int res; + + if (!mnt) { + fprintf(stderr, "fuse: missing mountpoint\n"); + return -1; + } + + res = lstat(mnt, &stbuf); + if (res == -1) { + fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n", + mnt, strerror(errno)); + return -1; + } + + if (!mo->nonempty) { + res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode, stbuf.st_size); + if (res == -1) + return -1; + } + + fd = open(devname, O_RDWR); + if (fd == -1) { + if (errno == ENODEV || errno == ENOENT) + fprintf(stderr, + "fuse: device not found, try 'modprobe fuse' first\n"); + else + fprintf(stderr, "fuse: failed to open %s: %s\n", devname, + strerror(errno)); + return -1; + } + + snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%i,group_id=%i", fd, + stbuf.st_mode & S_IFMT, getuid(), getgid()); + + res = fuse_opt_add_opt(&mo->kernel_opts, tmp); + if (res == -1) + goto out_close; + + source = malloc((mo->fsname ? strlen(mo->fsname) : 0) + + (mo->subtype ? strlen(mo->subtype) : 0) + + strlen(devname) + 32); + + type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32); + if (!type || !source) { + fprintf(stderr, "fuse: failed to allocate memory\n"); + goto out_close; + } + + strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); + if (mo->subtype) { + strcat(type, "."); + strcat(type, mo->subtype); + } + strcpy(source, + mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname)); + + /* JPA added two final zeroes */ + res = mount(source, mnt, MS_OPTIONSTR|mo->flags, type, NULL, 0, + mo->kernel_opts, MAX_MNTOPT_STR, 0, 0); + + if (res == -1 && errno == EINVAL && mo->subtype) { + /* Probably missing subtype support */ + strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); + if (mo->fsname) { + if (!mo->blkdev) + sprintf(source, "%s#%s", mo->subtype, mo->fsname); + } else { + strcpy(source, type); + } + /* JPA two null args added */ + res = mount(source, mnt, MS_OPTIONSTR|mo->flags, type, NULL, 0, + mo->kernel_opts, MAX_MNTOPT_STR, 0, 0); + } + if (res == -1) { + /* + * Maybe kernel doesn't support unprivileged mounts, in this + * case try falling back to fusermount + */ + if (errno == EPERM) { + res = -2; + } else { + int errno_save = errno; + if (mo->blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk()) + fprintf(stderr, "fuse: 'fuseblk' support missing\n"); + else + fprintf(stderr, "fuse: mount failed: %s\n", + strerror(errno_save)); + } + + goto out_close; + } + + return fd; + + out_umount: + umount2(mnt, 2); /* lazy umount */ + out_close: + free(type); + free(source); + close(fd); + return res; +} + +#endif /* __SOLARIS__ */ + +static int get_mnt_flag_opts(char **mnt_optsp, int flags) +{ + int i; + + if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1) + return -1; + + for (i = 0; mount_flags[i].opt != NULL; i++) { + if (mount_flags[i].on && (flags & mount_flags[i].flag) && + fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1) + return -1; + } + return 0; +} + +int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) +{ + struct mount_opts mo; + int res = -1; + char *mnt_opts = NULL; +#ifdef __SOLARIS__ + struct solaris_mount_opts smo; + struct fuse_args sa = FUSE_ARGS_INIT(0, NULL); +#endif /* __SOLARIS__ */ + + memset(&mo, 0, sizeof(mo)); +#ifndef __SOLARIS__ + if (getuid()) + mo.flags = MS_NOSUID | MS_NODEV; +#else /* __SOLARIS__ */ + mo.flags = 0; + memset(&smo, 0, sizeof(smo)); + if (args != NULL) { + while (args->argv[sa.argc] != NULL) + fuse_opt_add_arg(&sa, args->argv[sa.argc]); + } +#endif /* __SOLARIS__ */ + + if (args && + fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1) +#ifndef __SOLARIS__ + return -1; +#else /* __SOLARIS__ */ + goto out; /* if SOLARIS, clean up 'sa' */ + + /* + * In Solaris, nosuid is equivalent to nosetuid + nodevices. We only + * have MS_NOSUID for mount flags (no MS_(NO)SETUID, etc.). But if + * we set that as a default, it restricts specifying just nosetuid + * or nodevices; there is no way for the user to specify setuid + + * nodevices or vice-verse. So we parse the existing options, then + * add restrictive defaults if needed. + */ + if (fuse_opt_parse(&sa, &smo, solaris_mnt_opts, NULL) == -1) + goto out; + if (smo.nosuid || (!smo.nodevices && !smo.devices + && !smo.nosetuid && !smo.setuid)) { + mo.flags |= MS_NOSUID; + } else { + /* + * Defaults; if neither nodevices|devices,nosetuid|setuid has + * been specified, add the default negative option string. If + * both have been specified (i.e., -osuid,nosuid), leave them + * alone; the last option will have precedence. + */ + if (!smo.nodevices && !smo.devices) + if (fuse_opt_add_opt(&mo.kernel_opts, "nodevices") == -1) + goto out; + if (!smo.nosetuid && !smo.setuid) + if (fuse_opt_add_opt(&mo.kernel_opts, "nosetuid") == -1) + goto out; + } +#endif /* __SOLARIS__ */ + + if (mo.allow_other && mo.allow_root) { + fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n"); + goto out; + } + res = 0; + if (mo.ishelp) + goto out; + + res = -1; + if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1) + goto out; +#ifndef __SOLARIS__ + if (!(mo.flags & MS_NODEV) && fuse_opt_add_opt(&mnt_opts, "dev") == -1) + goto out; + if (!(mo.flags & MS_NOSUID) && fuse_opt_add_opt(&mnt_opts, "suid") == -1) + goto out; + if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1) + goto out; + if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1) + goto out; + if (mo.fusermount_opts && fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) < 0) + goto out; + res = fusermount(0, 0, 0, mnt_opts ? mnt_opts : "", mountpoint); +#else /* __SOLARIS__ */ + if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1) + goto out; + if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1) + goto out; + res = fuse_mount_sys(mountpoint, &mo, mnt_opts); + if (res == -2) { + if (mo.fusermount_opts && + fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1) + goto out; + + if (mo.subtype) { + char *tmp_opts = NULL; + + res = -1; + if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 || + fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) { + free(tmp_opts); + goto out; + } + + res = fuse_mount_fusermount(mountpoint, tmp_opts, 1); + free(tmp_opts); + if (res == -1) + res = fuse_mount_fusermount(mountpoint, mnt_opts, 0); + } else { + res = fuse_mount_fusermount(mountpoint, mnt_opts, 0); + } + } +#endif /* __SOLARIS__ */ + +out: + free(mnt_opts); +#ifdef __SOLARIS__ + fuse_opt_free_args(&sa); + free(mo.subtype); + free(mo.subtype_opt); +#endif /* __SOLARIS__ */ + free(mo.fsname); + free(mo.fusermount_opts); + free(mo.kernel_opts); + free(mo.mtab_opts); + return res; +} diff --git a/libfuse-lite/mount_util.c b/libfuse-lite/mount_util.c new file mode 100755 index 0000000000000000000000000000000000000000..1a7ac3c7d1284ee242d7456632d9bedefc416d65 --- /dev/null +++ b/libfuse-lite/mount_util.c @@ -0,0 +1,459 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include "config.h" +#include "mount_util.h" +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/wait.h> +#ifdef __SOLARIS__ +#else /* __SOLARIS__ */ +#include <mntent.h> +#include <sys/mount.h> +#include <sys/param.h> +#endif /* __SOLARIS__ */ + +#ifdef __SOLARIS__ + +char *mkdtemp(char *template); + +#ifndef _PATH_MOUNTED +#define _PATH_MOUNTED "/etc/mnttab" +#endif /* _PATH_MOUNTED */ + +#ifndef IGNORE_MTAB +static int mtab_needs_update(const char *mnt) +{ + struct stat stbuf; + + /* If mtab is within new mount, don't touch it */ + if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 && + _PATH_MOUNTED[strlen(mnt)] == '/') + return 0; + + if (lstat(_PATH_MOUNTED, &stbuf) != -1 && S_ISLNK(stbuf.st_mode)) + return 0; + + return 1; +} +#endif /* IGNORE_MTAB */ + +int fuse_mnt_add_mount(const char *progname, const char *fsname, + const char *mnt, const char *type, const char *opts) +{ + int res; + int status; + +#ifndef IGNORE_MTAB + if (!mtab_needs_update(mnt)) + return 0; +#endif /* IGNORE_MTAB */ + + res = fork(); + if (res == -1) { + fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); + return -1; + } + if (res == 0) { + char templ[] = "/tmp/fusermountXXXXXX"; + char *tmp; + + setuid(geteuid()); + + /* + * hide in a directory, where mount isn't able to resolve + * fsname as a valid path + */ + tmp = mkdtemp(templ); + if (!tmp) { + fprintf(stderr, "%s: failed to create temporary directory\n", + progname); + exit(1); + } + if (chdir(tmp)) { + fprintf(stderr, "%s: failed to chdir to %s: %s\n", + progname, tmp, strerror(errno)); + exit(1); + } + rmdir(tmp); + execl("/sbin/mount", "/sbin/mount", "-F", type, "-o", opts, + fsname, mnt, NULL); + fprintf(stderr, "%s: failed to execute /sbin/mount: %s\n", progname, + strerror(errno)); + exit(1); + } + res = waitpid(res, &status, 0); + if (res == -1) { + fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); + return -1; + } + if (status != 0) + return -1; + + return 0; +} + +int fuse_mnt_umount(const char *progname, const char *mnt, int lazy) +{ + int res; + int status; + +#ifndef IGNORE_MTAB + if (!mtab_needs_update(mnt)) + return 0; +#endif /* IGNORE_MTAB */ + + res = fork(); + if (res == -1) { + fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); + return -1; + } + if (res == 0) { + setuid(geteuid()); + execl("/sbin/umount", "/sbin/umount", !lazy ? "-f" : NULL, mnt, + NULL); + fprintf(stderr, "%s: failed to execute /sbin/umount: %s\n", progname, + strerror(errno)); + exit(1); + } + res = waitpid(res, &status, 0); + if (res == -1) { + fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); + return -1; + } + if (status != 0) + return -1; + + return 0; +} + +char *fuse_mnt_resolve_path(const char *progname, const char *orig) +{ + char buf[PATH_MAX]; + char *copy; + char *dst; + char *end; + char *lastcomp; + const char *toresolv; + + if (!orig[0]) { + fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig); + return NULL; + } + + copy = strdup(orig); + if (copy == NULL) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return NULL; + } + + toresolv = copy; + lastcomp = NULL; + for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --); + if (end[0] != '/') { + char *tmp; + end[1] = '\0'; + tmp = strrchr(copy, '/'); + if (tmp == NULL) { + lastcomp = copy; + toresolv = "."; + } else { + lastcomp = tmp + 1; + if (tmp == copy) + toresolv = "/"; + } + if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) { + lastcomp = NULL; + toresolv = copy; + } + else if (tmp) + tmp[0] = '\0'; + } + if (realpath(toresolv, buf) == NULL) { + fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig, + strerror(errno)); + free(copy); + return NULL; + } + if (lastcomp == NULL) + dst = strdup(buf); + else { + dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1); + if (dst) { + unsigned buflen = strlen(buf); + if (buflen && buf[buflen-1] == '/') + sprintf(dst, "%s%s", buf, lastcomp); + else + sprintf(dst, "%s/%s", buf, lastcomp); + } + } + free(copy); + if (dst == NULL) + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return dst; +} + +int fuse_mnt_check_empty(const char *progname, const char *mnt, + mode_t rootmode, off_t rootsize) +{ + int isempty = 1; + + if (S_ISDIR(rootmode)) { + struct dirent *ent; + DIR *dp = opendir(mnt); + if (dp == NULL) { + fprintf(stderr, "%s: failed to open mountpoint for reading: %s\n", + progname, strerror(errno)); + return -1; + } + while ((ent = readdir(dp)) != NULL) { + if (strcmp(ent->d_name, ".") != 0 && + strcmp(ent->d_name, "..") != 0) { + isempty = 0; + break; + } + } + closedir(dp); + } else if (rootsize) + isempty = 0; + + if (!isempty) { + fprintf(stderr, "%s: mountpoint is not empty\n", progname); + fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname); + return -1; + } + return 0; +} + +int fuse_mnt_check_fuseblk(void) +{ + char buf[256]; + FILE *f = fopen("/proc/filesystems", "r"); + if (!f) + return 1; + + while (fgets(buf, sizeof(buf), f)) + if (strstr(buf, "fuseblk\n")) { + fclose(f); + return 1; + } + + fclose(f); + return 0; +} + +#else /* __SOLARIS__ */ + +static int mtab_needs_update(const char *mnt) +{ + int res; + struct stat stbuf; + + /* If mtab is within new mount, don't touch it */ + if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 && + _PATH_MOUNTED[strlen(mnt)] == '/') + return 0; + + /* + * Skip mtab update if /etc/mtab: + * + * - doesn't exist, + * - is a symlink, + * - is on a read-only filesystem. + */ + res = lstat(_PATH_MOUNTED, &stbuf); + if (res == -1) { + if (errno == ENOENT) + return 0; + } else { + if (S_ISLNK(stbuf.st_mode)) + return 0; + + res = access(_PATH_MOUNTED, W_OK); + if (res == -1 && errno == EROFS) + return 0; + } + + return 1; +} + +int fuse_mnt_add_mount(const char *progname, const char *fsname, + const char *mnt, const char *type, const char *opts) +{ + int res; + + if (!mtab_needs_update(mnt)) + return 0; + + res = fork(); + if (res == -1) { + fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); + return 0; + } + if (res == 0) { + char templ[] = "/tmp/fusermountXXXXXX"; + char *tmp; + + if (setuid(geteuid())) + fprintf(stderr, "%s: failed to setuid : %s\n", progname, + strerror(errno)); + + /* + * hide in a directory, where mount isn't able to resolve + * fsname as a valid path + */ + tmp = mkdtemp(templ); + if (!tmp) { + fprintf(stderr, "%s: failed to create temporary directory\n", + progname); + exit(1); + } + if (chdir(tmp)) { + fprintf(stderr, "%s: failed to chdir to %s: %s\n", + progname, tmp, strerror(errno)); + exit(1); + } + rmdir(tmp); + execl("/bin/mount", "/bin/mount", "-i", "-f", "-t", type, "-o", opts, + fsname, mnt, NULL); + fprintf(stderr, "%s: failed to execute /bin/mount: %s\n", progname, + strerror(errno)); + exit(1); + } + return 0; +} + +int fuse_mnt_umount(const char *progname, const char *mnt, int lazy) +{ + int res; + int status; + + if (!mtab_needs_update(mnt)) { + res = umount2(mnt, lazy ? 2 : 0); + if (res == -1) + fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, + mnt, strerror(errno)); + return res; + } + + res = fork(); + if (res == -1) { + fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); + return -1; + } + if (res == 0) { + if (setuid(geteuid())) + fprintf(stderr, "%s: failed to setuid : %s\n", progname, + strerror(errno)); + execl("/bin/umount", "/bin/umount", "-i", mnt, lazy ? "-l" : NULL, + NULL); + fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", progname, + strerror(errno)); + exit(1); + } + res = waitpid(res, &status, 0); + if (res == -1) { + fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno)); + return -1; + } + if (status != 0) + return -1; + + return 0; +} + +char *fuse_mnt_resolve_path(const char *progname, const char *orig) +{ + char buf[PATH_MAX]; + char *copy; + char *dst; + char *end; + char *lastcomp; + const char *toresolv; + + if (!orig[0]) { + fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig); + return NULL; + } + + copy = strdup(orig); + if (copy == NULL) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return NULL; + } + + toresolv = copy; + lastcomp = NULL; + for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --); + if (end[0] != '/') { + char *tmp; + end[1] = '\0'; + tmp = strrchr(copy, '/'); + if (tmp == NULL) { + lastcomp = copy; + toresolv = "."; + } else { + lastcomp = tmp + 1; + if (tmp == copy) + toresolv = "/"; + } + if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) { + lastcomp = NULL; + toresolv = copy; + } + else if (tmp) + tmp[0] = '\0'; + } + if (realpath(toresolv, buf) == NULL) { + fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig, + strerror(errno)); + free(copy); + return NULL; + } + if (lastcomp == NULL) + dst = strdup(buf); + else { + dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1); + if (dst) { + unsigned buflen = strlen(buf); + if (buflen && buf[buflen-1] == '/') + sprintf(dst, "%s%s", buf, lastcomp); + else + sprintf(dst, "%s/%s", buf, lastcomp); + } + } + free(copy); + if (dst == NULL) + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return dst; +} + +int fuse_mnt_check_fuseblk(void) +{ + char buf[256]; + FILE *f = fopen("/proc/filesystems", "r"); + if (!f) + return 1; + + while (fgets(buf, sizeof(buf), f)) + if (strstr(buf, "fuseblk\n")) { + fclose(f); + return 1; + } + + fclose(f); + return 0; +} + +#endif /* __SOLARIS__ */ diff --git a/libfuse-lite/mount_util.h b/libfuse-lite/mount_util.h new file mode 100755 index 0000000000000000000000000000000000000000..0318385f9b78aa252d3dc6e4060db3f8d4a1d34d --- /dev/null +++ b/libfuse-lite/mount_util.h @@ -0,0 +1,27 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include <sys/types.h> + +int fuse_mnt_add_mount(const char *progname, const char *fsname, + const char *mnt, const char *type, const char *opts); +int fuse_mnt_umount(const char *progname, const char *mnt, int lazy); +char *fuse_mnt_resolve_path(const char *progname, const char *orig); +int fuse_mnt_check_fuseblk(void); + +#ifdef __SOLARIS__ + +int fuse_mnt_check_empty(const char *progname, const char *mnt, + mode_t rootmode, off_t rootsize); + +#else /* __SOLARIS__ */ + +int fusermount(int unmount, int quiet, int lazy, const char *opts, + const char *origmnt); + +#endif /* __SOLARIS__ */ diff --git a/libntfs-3g/Android.mk b/libntfs-3g/Android.mk new file mode 100755 index 0000000000000000000000000000000000000000..c9962dd4f1953fc3cba4381cc806cc76c3817696 --- /dev/null +++ b/libntfs-3g/Android.mk @@ -0,0 +1,57 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + acls.c \ + attrib.c \ + attrlist.c \ + bitmap.c \ + bootsect.c \ + cache.c \ + collate.c \ + compat.c \ + compress.c \ + debug.c \ + device.c \ + dir.c \ + ea.c \ + efs.c \ + ioctl.c \ + index.c \ + inode.c \ + lcnalloc.c \ + logfile.c \ + logging.c \ + mft.c \ + misc.c \ + mst.c \ + object_id.c \ + reparse.c \ + runlist.c \ + realpath.c \ + security.c \ + unistr.c \ + volume.c \ + xattrs.c \ + unix_io.c + +LOCAL_MODULE := libntfs-3g +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := \ + libc \ + libutils \ + liblog + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/../src \ + $(LOCAL_PATH)/../include/ntfs-3g + +LOCAL_CFLAGS := -O2 -g -W -Wall \ + -DHAVE_CONFIG_H \ + -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) +LOCAL_PRELINK_MODULE := false + +include $(BUILD_SHARED_LIBRARY) + diff --git a/libntfs-3g/Makefile.am b/libntfs-3g/Makefile.am new file mode 100755 index 0000000000000000000000000000000000000000..d6b150e59bc58cb17ac04b9914566bba9a76ce2b --- /dev/null +++ b/libntfs-3g/Makefile.am @@ -0,0 +1,84 @@ + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in + +if INSTALL_LIBRARY +rootlib_LTLIBRARIES=#Create directory +lib_LTLIBRARIES = libntfs-3g.la +pkgconfig_DATA = libntfs-3g.pc +else +noinst_LTLIBRARIES = libntfs-3g.la +endif + +libntfs_3g_la_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g +libntfs_3g_la_CPPFLAGS= $(AM_CPPFLAGS) $(LIBNTFS_CPPFLAGS) +libntfs_3g_la_LIBADD = $(LIBNTFS_LIBS) +libntfs_3g_la_LDFLAGS = -version-info $(LIBNTFS_3G_VERSION) -no-undefined + +libntfs_3g_la_SOURCES = \ + acls.c \ + attrib.c \ + attrlist.c \ + bitmap.c \ + bootsect.c \ + cache.c \ + collate.c \ + compat.c \ + compress.c \ + debug.c \ + device.c \ + dir.c \ + ea.c \ + efs.c \ + index.c \ + inode.c \ + ioctl.c \ + lcnalloc.c \ + logfile.c \ + logging.c \ + mft.c \ + misc.c \ + mst.c \ + object_id.c \ + realpath.c \ + reparse.c \ + runlist.c \ + security.c \ + unistr.c \ + volume.c \ + xattrs.c + +if NTFS_DEVICE_DEFAULT_IO_OPS +if WINDOWS +libntfs_3g_la_SOURCES += win32_io.c +else +libntfs_3g_la_SOURCES += unix_io.c +endif +endif + +# We may need to move .so files to root +# And create ldscript or symbolic link from /usr +install-exec-hook: install-rootlibLTLIBRARIES +if INSTALL_LIBRARY + if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \ + $(MV) -f "$(DESTDIR)/$(libdir)"/libntfs-3g.so* "$(DESTDIR)/$(rootlibdir)"; \ + fi +if GENERATE_LDSCRIPT + if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \ + $(install_sh_PROGRAM) "libntfs-3g.script.so" "$(DESTDIR)/$(libdir)/libntfs-3g.so"; \ + fi +else + if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \ + $(LN_S) "$(rootlibdir)/libntfs-3g.so" "$(DESTDIR)/$(libdir)/libntfs-3g.so"; \ + fi +endif +endif + +uninstall-local: +if INSTALL_LIBRARY + $(RM) -f "$(DESTDIR)/$(rootlibdir)"/libntfs-3g.so* +endif + +if ENABLE_NTFSPROGS +libs: $(lib_LTLIBRARIES) +endif + diff --git a/libntfs-3g/Makefile.in b/libntfs-3g/Makefile.in new file mode 100755 index 0000000000000000000000000000000000000000..1ae6bca41388cf87530dfd2f148a4ad74a335f03 --- /dev/null +++ b/libntfs-3g/Makefile.in @@ -0,0 +1,1069 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@NTFS_DEVICE_DEFAULT_IO_OPS_TRUE@@WINDOWS_TRUE@am__append_1 = win32_io.c +@NTFS_DEVICE_DEFAULT_IO_OPS_TRUE@@WINDOWS_FALSE@am__append_2 = unix_io.c +subdir = libntfs-3g +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/libntfs-3g.pc.in $(srcdir)/libntfs-3g.script.so.in \ + $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = libntfs-3g.pc libntfs-3g.script.so +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(rootlibdir)" \ + "$(DESTDIR)$(pkgconfigdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) \ + $(rootlib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libntfs_3g_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am__libntfs_3g_la_SOURCES_DIST = acls.c attrib.c attrlist.c bitmap.c \ + bootsect.c cache.c collate.c compat.c compress.c debug.c \ + device.c dir.c ea.c efs.c index.c inode.c ioctl.c lcnalloc.c \ + logfile.c logging.c mft.c misc.c mst.c object_id.c realpath.c \ + reparse.c runlist.c security.c unistr.c volume.c xattrs.c \ + win32_io.c unix_io.c +@NTFS_DEVICE_DEFAULT_IO_OPS_TRUE@@WINDOWS_TRUE@am__objects_1 = libntfs_3g_la-win32_io.lo +@NTFS_DEVICE_DEFAULT_IO_OPS_TRUE@@WINDOWS_FALSE@am__objects_2 = libntfs_3g_la-unix_io.lo +am_libntfs_3g_la_OBJECTS = libntfs_3g_la-acls.lo \ + libntfs_3g_la-attrib.lo libntfs_3g_la-attrlist.lo \ + libntfs_3g_la-bitmap.lo libntfs_3g_la-bootsect.lo \ + libntfs_3g_la-cache.lo libntfs_3g_la-collate.lo \ + libntfs_3g_la-compat.lo libntfs_3g_la-compress.lo \ + libntfs_3g_la-debug.lo libntfs_3g_la-device.lo \ + libntfs_3g_la-dir.lo libntfs_3g_la-ea.lo libntfs_3g_la-efs.lo \ + libntfs_3g_la-index.lo libntfs_3g_la-inode.lo \ + libntfs_3g_la-ioctl.lo libntfs_3g_la-lcnalloc.lo \ + libntfs_3g_la-logfile.lo libntfs_3g_la-logging.lo \ + libntfs_3g_la-mft.lo libntfs_3g_la-misc.lo \ + libntfs_3g_la-mst.lo libntfs_3g_la-object_id.lo \ + libntfs_3g_la-realpath.lo libntfs_3g_la-reparse.lo \ + libntfs_3g_la-runlist.lo libntfs_3g_la-security.lo \ + libntfs_3g_la-unistr.lo libntfs_3g_la-volume.lo \ + libntfs_3g_la-xattrs.lo $(am__objects_1) $(am__objects_2) +libntfs_3g_la_OBJECTS = $(am_libntfs_3g_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libntfs_3g_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libntfs_3g_la_CFLAGS) \ + $(CFLAGS) $(libntfs_3g_la_LDFLAGS) $(LDFLAGS) -o $@ +@INSTALL_LIBRARY_FALSE@am_libntfs_3g_la_rpath = +@INSTALL_LIBRARY_TRUE@am_libntfs_3g_la_rpath = -rpath $(libdir) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libntfs_3g_la_SOURCES) +DIST_SOURCES = $(am__libntfs_3g_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FUSE_MODULE_CFLAGS = @FUSE_MODULE_CFLAGS@ +FUSE_MODULE_LIBS = @FUSE_MODULE_LIBS@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDCONFIG = @LDCONFIG@ +LDFLAGS = @LDFLAGS@ +LIBFUSE_LITE_CFLAGS = @LIBFUSE_LITE_CFLAGS@ +LIBFUSE_LITE_LIBS = @LIBFUSE_LITE_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBNTFS_3G_VERSION = @LIBNTFS_3G_VERSION@ +LIBNTFS_CPPFLAGS = @LIBNTFS_CPPFLAGS@ +LIBNTFS_LIBS = @LIBNTFS_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MKNTFS_CPPFLAGS = @MKNTFS_CPPFLAGS@ +MKNTFS_LIBS = @MKNTFS_LIBS@ +MV = @MV@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NTFSPROGS_STATIC_LIBS = @NTFSPROGS_STATIC_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OUTPUT_FORMAT = @OUTPUT_FORMAT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +RM = @RM@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +all_includes = @all_includes@ +all_libraries = @all_libraries@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +ntfs3gincludedir = @ntfs3gincludedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rootbindir = @rootbindir@ +rootlibdir = @rootlibdir@ +rootsbindir = @rootsbindir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +@INSTALL_LIBRARY_TRUE@rootlib_LTLIBRARIES = #Create directory +@INSTALL_LIBRARY_TRUE@lib_LTLIBRARIES = libntfs-3g.la +@INSTALL_LIBRARY_TRUE@pkgconfig_DATA = libntfs-3g.pc +@INSTALL_LIBRARY_FALSE@noinst_LTLIBRARIES = libntfs-3g.la +libntfs_3g_la_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g +libntfs_3g_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBNTFS_CPPFLAGS) +libntfs_3g_la_LIBADD = $(LIBNTFS_LIBS) +libntfs_3g_la_LDFLAGS = -version-info $(LIBNTFS_3G_VERSION) -no-undefined +libntfs_3g_la_SOURCES = acls.c attrib.c attrlist.c bitmap.c bootsect.c \ + cache.c collate.c compat.c compress.c debug.c device.c dir.c \ + ea.c efs.c index.c inode.c ioctl.c lcnalloc.c logfile.c \ + logging.c mft.c misc.c mst.c object_id.c realpath.c reparse.c \ + runlist.c security.c unistr.c volume.c xattrs.c \ + $(am__append_1) $(am__append_2) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libntfs-3g/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu libntfs-3g/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +libntfs-3g.pc: $(top_builddir)/config.status $(srcdir)/libntfs-3g.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +libntfs-3g.script.so: $(top_builddir)/config.status $(srcdir)/libntfs-3g.script.so.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +install-rootlibLTLIBRARIES: $(rootlib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(rootlib_LTLIBRARIES)'; test -n "$(rootlibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(rootlibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(rootlibdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(rootlibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(rootlibdir)"; \ + } + +uninstall-rootlibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(rootlib_LTLIBRARIES)'; test -n "$(rootlibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(rootlibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(rootlibdir)/$$f"; \ + done + +clean-rootlibLTLIBRARIES: + -test -z "$(rootlib_LTLIBRARIES)" || rm -f $(rootlib_LTLIBRARIES) + @list='$(rootlib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libntfs-3g.la: $(libntfs_3g_la_OBJECTS) $(libntfs_3g_la_DEPENDENCIES) $(EXTRA_libntfs_3g_la_DEPENDENCIES) + $(AM_V_CCLD)$(libntfs_3g_la_LINK) $(am_libntfs_3g_la_rpath) $(libntfs_3g_la_OBJECTS) $(libntfs_3g_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-acls.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-attrib.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-attrlist.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-bitmap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-bootsect.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-cache.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-collate.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-compat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-compress.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-debug.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-device.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-dir.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-ea.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-efs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-index.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-inode.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-ioctl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-lcnalloc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-logfile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-logging.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-mft.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-misc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-mst.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-object_id.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-realpath.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-reparse.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-runlist.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-security.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-unistr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-unix_io.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-volume.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-win32_io.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libntfs_3g_la-xattrs.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libntfs_3g_la-acls.lo: acls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-acls.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-acls.Tpo -c -o libntfs_3g_la-acls.lo `test -f 'acls.c' || echo '$(srcdir)/'`acls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-acls.Tpo $(DEPDIR)/libntfs_3g_la-acls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='acls.c' object='libntfs_3g_la-acls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-acls.lo `test -f 'acls.c' || echo '$(srcdir)/'`acls.c + +libntfs_3g_la-attrib.lo: attrib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-attrib.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-attrib.Tpo -c -o libntfs_3g_la-attrib.lo `test -f 'attrib.c' || echo '$(srcdir)/'`attrib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-attrib.Tpo $(DEPDIR)/libntfs_3g_la-attrib.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attrib.c' object='libntfs_3g_la-attrib.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-attrib.lo `test -f 'attrib.c' || echo '$(srcdir)/'`attrib.c + +libntfs_3g_la-attrlist.lo: attrlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-attrlist.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-attrlist.Tpo -c -o libntfs_3g_la-attrlist.lo `test -f 'attrlist.c' || echo '$(srcdir)/'`attrlist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-attrlist.Tpo $(DEPDIR)/libntfs_3g_la-attrlist.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attrlist.c' object='libntfs_3g_la-attrlist.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-attrlist.lo `test -f 'attrlist.c' || echo '$(srcdir)/'`attrlist.c + +libntfs_3g_la-bitmap.lo: bitmap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-bitmap.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-bitmap.Tpo -c -o libntfs_3g_la-bitmap.lo `test -f 'bitmap.c' || echo '$(srcdir)/'`bitmap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-bitmap.Tpo $(DEPDIR)/libntfs_3g_la-bitmap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bitmap.c' object='libntfs_3g_la-bitmap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-bitmap.lo `test -f 'bitmap.c' || echo '$(srcdir)/'`bitmap.c + +libntfs_3g_la-bootsect.lo: bootsect.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-bootsect.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-bootsect.Tpo -c -o libntfs_3g_la-bootsect.lo `test -f 'bootsect.c' || echo '$(srcdir)/'`bootsect.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-bootsect.Tpo $(DEPDIR)/libntfs_3g_la-bootsect.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bootsect.c' object='libntfs_3g_la-bootsect.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-bootsect.lo `test -f 'bootsect.c' || echo '$(srcdir)/'`bootsect.c + +libntfs_3g_la-cache.lo: cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-cache.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-cache.Tpo -c -o libntfs_3g_la-cache.lo `test -f 'cache.c' || echo '$(srcdir)/'`cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-cache.Tpo $(DEPDIR)/libntfs_3g_la-cache.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cache.c' object='libntfs_3g_la-cache.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-cache.lo `test -f 'cache.c' || echo '$(srcdir)/'`cache.c + +libntfs_3g_la-collate.lo: collate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-collate.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-collate.Tpo -c -o libntfs_3g_la-collate.lo `test -f 'collate.c' || echo '$(srcdir)/'`collate.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-collate.Tpo $(DEPDIR)/libntfs_3g_la-collate.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='collate.c' object='libntfs_3g_la-collate.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-collate.lo `test -f 'collate.c' || echo '$(srcdir)/'`collate.c + +libntfs_3g_la-compat.lo: compat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-compat.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-compat.Tpo -c -o libntfs_3g_la-compat.lo `test -f 'compat.c' || echo '$(srcdir)/'`compat.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-compat.Tpo $(DEPDIR)/libntfs_3g_la-compat.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat.c' object='libntfs_3g_la-compat.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-compat.lo `test -f 'compat.c' || echo '$(srcdir)/'`compat.c + +libntfs_3g_la-compress.lo: compress.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-compress.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-compress.Tpo -c -o libntfs_3g_la-compress.lo `test -f 'compress.c' || echo '$(srcdir)/'`compress.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-compress.Tpo $(DEPDIR)/libntfs_3g_la-compress.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compress.c' object='libntfs_3g_la-compress.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-compress.lo `test -f 'compress.c' || echo '$(srcdir)/'`compress.c + +libntfs_3g_la-debug.lo: debug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-debug.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-debug.Tpo -c -o libntfs_3g_la-debug.lo `test -f 'debug.c' || echo '$(srcdir)/'`debug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-debug.Tpo $(DEPDIR)/libntfs_3g_la-debug.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='debug.c' object='libntfs_3g_la-debug.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-debug.lo `test -f 'debug.c' || echo '$(srcdir)/'`debug.c + +libntfs_3g_la-device.lo: device.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-device.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-device.Tpo -c -o libntfs_3g_la-device.lo `test -f 'device.c' || echo '$(srcdir)/'`device.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-device.Tpo $(DEPDIR)/libntfs_3g_la-device.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='device.c' object='libntfs_3g_la-device.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-device.lo `test -f 'device.c' || echo '$(srcdir)/'`device.c + +libntfs_3g_la-dir.lo: dir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-dir.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-dir.Tpo -c -o libntfs_3g_la-dir.lo `test -f 'dir.c' || echo '$(srcdir)/'`dir.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-dir.Tpo $(DEPDIR)/libntfs_3g_la-dir.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dir.c' object='libntfs_3g_la-dir.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-dir.lo `test -f 'dir.c' || echo '$(srcdir)/'`dir.c + +libntfs_3g_la-ea.lo: ea.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-ea.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-ea.Tpo -c -o libntfs_3g_la-ea.lo `test -f 'ea.c' || echo '$(srcdir)/'`ea.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-ea.Tpo $(DEPDIR)/libntfs_3g_la-ea.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ea.c' object='libntfs_3g_la-ea.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-ea.lo `test -f 'ea.c' || echo '$(srcdir)/'`ea.c + +libntfs_3g_la-efs.lo: efs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-efs.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-efs.Tpo -c -o libntfs_3g_la-efs.lo `test -f 'efs.c' || echo '$(srcdir)/'`efs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-efs.Tpo $(DEPDIR)/libntfs_3g_la-efs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='efs.c' object='libntfs_3g_la-efs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-efs.lo `test -f 'efs.c' || echo '$(srcdir)/'`efs.c + +libntfs_3g_la-index.lo: index.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-index.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-index.Tpo -c -o libntfs_3g_la-index.lo `test -f 'index.c' || echo '$(srcdir)/'`index.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-index.Tpo $(DEPDIR)/libntfs_3g_la-index.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='index.c' object='libntfs_3g_la-index.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-index.lo `test -f 'index.c' || echo '$(srcdir)/'`index.c + +libntfs_3g_la-inode.lo: inode.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-inode.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-inode.Tpo -c -o libntfs_3g_la-inode.lo `test -f 'inode.c' || echo '$(srcdir)/'`inode.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-inode.Tpo $(DEPDIR)/libntfs_3g_la-inode.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inode.c' object='libntfs_3g_la-inode.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-inode.lo `test -f 'inode.c' || echo '$(srcdir)/'`inode.c + +libntfs_3g_la-ioctl.lo: ioctl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-ioctl.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-ioctl.Tpo -c -o libntfs_3g_la-ioctl.lo `test -f 'ioctl.c' || echo '$(srcdir)/'`ioctl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-ioctl.Tpo $(DEPDIR)/libntfs_3g_la-ioctl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ioctl.c' object='libntfs_3g_la-ioctl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-ioctl.lo `test -f 'ioctl.c' || echo '$(srcdir)/'`ioctl.c + +libntfs_3g_la-lcnalloc.lo: lcnalloc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-lcnalloc.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-lcnalloc.Tpo -c -o libntfs_3g_la-lcnalloc.lo `test -f 'lcnalloc.c' || echo '$(srcdir)/'`lcnalloc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-lcnalloc.Tpo $(DEPDIR)/libntfs_3g_la-lcnalloc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lcnalloc.c' object='libntfs_3g_la-lcnalloc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-lcnalloc.lo `test -f 'lcnalloc.c' || echo '$(srcdir)/'`lcnalloc.c + +libntfs_3g_la-logfile.lo: logfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-logfile.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-logfile.Tpo -c -o libntfs_3g_la-logfile.lo `test -f 'logfile.c' || echo '$(srcdir)/'`logfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-logfile.Tpo $(DEPDIR)/libntfs_3g_la-logfile.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logfile.c' object='libntfs_3g_la-logfile.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-logfile.lo `test -f 'logfile.c' || echo '$(srcdir)/'`logfile.c + +libntfs_3g_la-logging.lo: logging.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-logging.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-logging.Tpo -c -o libntfs_3g_la-logging.lo `test -f 'logging.c' || echo '$(srcdir)/'`logging.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-logging.Tpo $(DEPDIR)/libntfs_3g_la-logging.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logging.c' object='libntfs_3g_la-logging.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-logging.lo `test -f 'logging.c' || echo '$(srcdir)/'`logging.c + +libntfs_3g_la-mft.lo: mft.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-mft.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-mft.Tpo -c -o libntfs_3g_la-mft.lo `test -f 'mft.c' || echo '$(srcdir)/'`mft.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-mft.Tpo $(DEPDIR)/libntfs_3g_la-mft.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mft.c' object='libntfs_3g_la-mft.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-mft.lo `test -f 'mft.c' || echo '$(srcdir)/'`mft.c + +libntfs_3g_la-misc.lo: misc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-misc.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-misc.Tpo -c -o libntfs_3g_la-misc.lo `test -f 'misc.c' || echo '$(srcdir)/'`misc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-misc.Tpo $(DEPDIR)/libntfs_3g_la-misc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='misc.c' object='libntfs_3g_la-misc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-misc.lo `test -f 'misc.c' || echo '$(srcdir)/'`misc.c + +libntfs_3g_la-mst.lo: mst.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-mst.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-mst.Tpo -c -o libntfs_3g_la-mst.lo `test -f 'mst.c' || echo '$(srcdir)/'`mst.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-mst.Tpo $(DEPDIR)/libntfs_3g_la-mst.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mst.c' object='libntfs_3g_la-mst.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-mst.lo `test -f 'mst.c' || echo '$(srcdir)/'`mst.c + +libntfs_3g_la-object_id.lo: object_id.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-object_id.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-object_id.Tpo -c -o libntfs_3g_la-object_id.lo `test -f 'object_id.c' || echo '$(srcdir)/'`object_id.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-object_id.Tpo $(DEPDIR)/libntfs_3g_la-object_id.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='object_id.c' object='libntfs_3g_la-object_id.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-object_id.lo `test -f 'object_id.c' || echo '$(srcdir)/'`object_id.c + +libntfs_3g_la-realpath.lo: realpath.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-realpath.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-realpath.Tpo -c -o libntfs_3g_la-realpath.lo `test -f 'realpath.c' || echo '$(srcdir)/'`realpath.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-realpath.Tpo $(DEPDIR)/libntfs_3g_la-realpath.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='realpath.c' object='libntfs_3g_la-realpath.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-realpath.lo `test -f 'realpath.c' || echo '$(srcdir)/'`realpath.c + +libntfs_3g_la-reparse.lo: reparse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-reparse.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-reparse.Tpo -c -o libntfs_3g_la-reparse.lo `test -f 'reparse.c' || echo '$(srcdir)/'`reparse.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-reparse.Tpo $(DEPDIR)/libntfs_3g_la-reparse.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='reparse.c' object='libntfs_3g_la-reparse.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-reparse.lo `test -f 'reparse.c' || echo '$(srcdir)/'`reparse.c + +libntfs_3g_la-runlist.lo: runlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-runlist.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-runlist.Tpo -c -o libntfs_3g_la-runlist.lo `test -f 'runlist.c' || echo '$(srcdir)/'`runlist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-runlist.Tpo $(DEPDIR)/libntfs_3g_la-runlist.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='runlist.c' object='libntfs_3g_la-runlist.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-runlist.lo `test -f 'runlist.c' || echo '$(srcdir)/'`runlist.c + +libntfs_3g_la-security.lo: security.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-security.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-security.Tpo -c -o libntfs_3g_la-security.lo `test -f 'security.c' || echo '$(srcdir)/'`security.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-security.Tpo $(DEPDIR)/libntfs_3g_la-security.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='security.c' object='libntfs_3g_la-security.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-security.lo `test -f 'security.c' || echo '$(srcdir)/'`security.c + +libntfs_3g_la-unistr.lo: unistr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-unistr.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-unistr.Tpo -c -o libntfs_3g_la-unistr.lo `test -f 'unistr.c' || echo '$(srcdir)/'`unistr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-unistr.Tpo $(DEPDIR)/libntfs_3g_la-unistr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unistr.c' object='libntfs_3g_la-unistr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-unistr.lo `test -f 'unistr.c' || echo '$(srcdir)/'`unistr.c + +libntfs_3g_la-volume.lo: volume.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-volume.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-volume.Tpo -c -o libntfs_3g_la-volume.lo `test -f 'volume.c' || echo '$(srcdir)/'`volume.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-volume.Tpo $(DEPDIR)/libntfs_3g_la-volume.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='volume.c' object='libntfs_3g_la-volume.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-volume.lo `test -f 'volume.c' || echo '$(srcdir)/'`volume.c + +libntfs_3g_la-xattrs.lo: xattrs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-xattrs.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-xattrs.Tpo -c -o libntfs_3g_la-xattrs.lo `test -f 'xattrs.c' || echo '$(srcdir)/'`xattrs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-xattrs.Tpo $(DEPDIR)/libntfs_3g_la-xattrs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xattrs.c' object='libntfs_3g_la-xattrs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-xattrs.lo `test -f 'xattrs.c' || echo '$(srcdir)/'`xattrs.c + +libntfs_3g_la-win32_io.lo: win32_io.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-win32_io.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-win32_io.Tpo -c -o libntfs_3g_la-win32_io.lo `test -f 'win32_io.c' || echo '$(srcdir)/'`win32_io.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-win32_io.Tpo $(DEPDIR)/libntfs_3g_la-win32_io.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='win32_io.c' object='libntfs_3g_la-win32_io.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-win32_io.lo `test -f 'win32_io.c' || echo '$(srcdir)/'`win32_io.c + +libntfs_3g_la-unix_io.lo: unix_io.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -MT libntfs_3g_la-unix_io.lo -MD -MP -MF $(DEPDIR)/libntfs_3g_la-unix_io.Tpo -c -o libntfs_3g_la-unix_io.lo `test -f 'unix_io.c' || echo '$(srcdir)/'`unix_io.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libntfs_3g_la-unix_io.Tpo $(DEPDIR)/libntfs_3g_la-unix_io.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unix_io.c' object='libntfs_3g_la-unix_io.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libntfs_3g_la_CPPFLAGS) $(CPPFLAGS) $(libntfs_3g_la_CFLAGS) $(CFLAGS) -c -o libntfs_3g_la-unix_io.lo `test -f 'unix_io.c' || echo '$(srcdir)/'`unix_io.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(rootlibdir)" "$(DESTDIR)$(pkgconfigdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES clean-rootlibLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkgconfigDATA install-rootlibLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES uninstall-local \ + uninstall-pkgconfigDATA uninstall-rootlibLTLIBRARIES + +.MAKE: install-am install-exec-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \ + clean-rootlibLTLIBRARIES cscopelist-am ctags ctags-am \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-exec-hook install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-man install-pdf install-pdf-am install-pkgconfigDATA \ + install-ps install-ps-am install-rootlibLTLIBRARIES \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-libLTLIBRARIES uninstall-local \ + uninstall-pkgconfigDATA uninstall-rootlibLTLIBRARIES + + +# We may need to move .so files to root +# And create ldscript or symbolic link from /usr +install-exec-hook: install-rootlibLTLIBRARIES +@INSTALL_LIBRARY_TRUE@ if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \ +@INSTALL_LIBRARY_TRUE@ $(MV) -f "$(DESTDIR)/$(libdir)"/libntfs-3g.so* "$(DESTDIR)/$(rootlibdir)"; \ +@INSTALL_LIBRARY_TRUE@ fi +@GENERATE_LDSCRIPT_TRUE@@INSTALL_LIBRARY_TRUE@ if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \ +@GENERATE_LDSCRIPT_TRUE@@INSTALL_LIBRARY_TRUE@ $(install_sh_PROGRAM) "libntfs-3g.script.so" "$(DESTDIR)/$(libdir)/libntfs-3g.so"; \ +@GENERATE_LDSCRIPT_TRUE@@INSTALL_LIBRARY_TRUE@ fi +@GENERATE_LDSCRIPT_FALSE@@INSTALL_LIBRARY_TRUE@ if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \ +@GENERATE_LDSCRIPT_FALSE@@INSTALL_LIBRARY_TRUE@ $(LN_S) "$(rootlibdir)/libntfs-3g.so" "$(DESTDIR)/$(libdir)/libntfs-3g.so"; \ +@GENERATE_LDSCRIPT_FALSE@@INSTALL_LIBRARY_TRUE@ fi + +uninstall-local: +@INSTALL_LIBRARY_TRUE@ $(RM) -f "$(DESTDIR)/$(rootlibdir)"/libntfs-3g.so* + +@ENABLE_NTFSPROGS_TRUE@libs: $(lib_LTLIBRARIES) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/libntfs-3g/acls.c b/libntfs-3g/acls.c new file mode 100755 index 0000000000000000000000000000000000000000..51a7e7f5919e9992068cf4810e47a738cf455a85 --- /dev/null +++ b/libntfs-3g/acls.c @@ -0,0 +1,4502 @@ +/** + * acls.c - General function to process NTFS ACLs + * + * This module is part of ntfs-3g library, but may also be + * integrated in tools running over Linux or Windows + * + * Copyright (c) 2007-2014 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H + /* + * integration into ntfs-3g + */ +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif +#include <unistd.h> +#include <pwd.h> +#include <grp.h> + +#include "types.h" +#include "layout.h" +#include "security.h" +#include "acls.h" +#include "misc.h" +#else + + /* + * integration into secaudit, check whether Win32, + * may have to be adapted to compiler or something else + */ + +#ifndef WIN32 +#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC) +#define WIN32 1 +#endif +#endif + +#include <stdio.h> +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <sys/types.h> +#include <errno.h> + + /* + * integration into secaudit/Win32 + */ +#ifdef WIN32 +#include <fcntl.h> +#include <windows.h> +#define __LITTLE_ENDIAN 1234 +#define __BYTE_ORDER __LITTLE_ENDIAN +#else + /* + * integration into secaudit/STSC + */ +#ifdef STSC +#include <stat.h> +#undef __BYTE_ORDER +#define __BYTE_ORDER __BIG_ENDIAN +#else + /* + * integration into secaudit/Linux + */ +#include <sys/stat.h> +#include <endian.h> +#include <unistd.h> +#include <dlfcn.h> +#endif /* STSC */ +#endif /* WIN32 */ +#include "secaudit.h" +#endif /* HAVE_CONFIG_H */ + +/* + * A few useful constants + */ + +/* + * null SID (S-1-0-0) + */ + +static const char nullsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 0, /* base */ + 0, 0, 0, 0 /* 1st level */ + }; + +static const SID *nullsid = (const SID*)nullsidbytes; + +/* + * SID for world (S-1-1-0) + */ + +static const char worldsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 1, /* base */ + 0, 0, 0, 0 /* 1st level */ +} ; + +const SID *worldsid = (const SID*)worldsidbytes; + +/* + * SID for authenticated user (S-1-5-11) + */ + +static const char authsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 11, 0, 0, 0 /* 1st level */ +}; + +static const SID *authsid = (const SID*)authsidbytes; + +/* + * SID for administrator + */ + +static const char adminsidbytes[] = { + 1, /* revision */ + 2, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 32, 0, 0, 0, /* 1st level */ + 32, 2, 0, 0 /* 2nd level */ +}; + +const SID *adminsid = (const SID*)adminsidbytes; + +/* + * SID for system + */ + +static const char systemsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 18, 0, 0, 0 /* 1st level */ + }; + +static const SID *systemsid = (const SID*)systemsidbytes; + +/* + * SID for generic creator-owner + * S-1-3-0 + */ + +static const char ownersidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 3, /* base */ + 0, 0, 0, 0 /* 1st level */ +} ; + +static const SID *ownersid = (const SID*)ownersidbytes; + +/* + * SID for generic creator-group + * S-1-3-1 + */ + +static const char groupsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 3, /* base */ + 1, 0, 0, 0 /* 1st level */ +} ; + +static const SID *groupsid = (const SID*)groupsidbytes; + +/* + * Determine the size of a SID + */ + +int ntfs_sid_size(const SID * sid) +{ + return (sid->sub_authority_count * 4 + 8); +} + +/* + * Test whether two SID are equal + */ + +BOOL ntfs_same_sid(const SID *first, const SID *second) +{ + int size; + + size = ntfs_sid_size(first); + return ((ntfs_sid_size(second) == size) + && !memcmp(first, second, size)); +} + +/* + * Test whether a SID means "world user" + * Local users group recognized as world + * Also interactive users so that /Users/Public is world accessible, + * but only if Posix ACLs are not enabled (if Posix ACLs are enabled, + * access to /Users/Public should be done by defining interactive users + * as a mapped group.) + */ + +static int is_world_sid(const SID * usid) +{ + return ( + /* check whether S-1-1-0 : world */ + ((usid->sub_authority_count == 1) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(1)) + && (usid->sub_authority[0] == const_cpu_to_le32(0))) + + /* check whether S-1-5-32-545 : local user */ + || ((usid->sub_authority_count == 2) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (usid->sub_authority[0] == const_cpu_to_le32(32)) + && (usid->sub_authority[1] == const_cpu_to_le32(545))) + + /* check whether S-1-5-11 : authenticated user */ + || ((usid->sub_authority_count == 1) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (usid->sub_authority[0] == const_cpu_to_le32(11))) + +#if !POSIXACLS + /* check whether S-1-5-4 : interactive user */ + || ((usid->sub_authority_count == 1) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (usid->sub_authority[0] == const_cpu_to_le32(4))) +#endif /* !POSIXACLS */ + ); +} + +/* + * Test whether a SID means "some user (or group)" + * Currently we only check for S-1-5-21... but we should + * probably test for other configurations + */ + +BOOL ntfs_is_user_sid(const SID *usid) +{ + return ((usid->sub_authority_count == 5) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (usid->sub_authority[0] == const_cpu_to_le32(21))); +} + +/* + * Test whether a SID means "some special group" + * Currently we only check for a few S-1-5-n but we should + * probably test for other configurations. + * + * This is useful for granting access to /Users/Public for + * specific users when the Posix ACLs are enabled. + */ + +static BOOL ntfs_known_group_sid(const SID *usid) +{ + /* count == 1 excludes S-1-5-5-X-Y (logon) */ + return ((usid->sub_authority_count == 1) + && (usid->identifier_authority.high_part == const_cpu_to_be16(0)) + && (usid->identifier_authority.low_part == const_cpu_to_be32(5)) + && (le32_to_cpu(usid->sub_authority[0]) >= 1) + && (le32_to_cpu(usid->sub_authority[0]) <= 6)); +} + +/* + * Determine the size of a security attribute + * whatever the order of fields + */ + +unsigned int ntfs_attr_size(const char *attr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pdacl; + const ACL *psacl; + const SID *psid; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + unsigned int endsid; + unsigned int endacl; + unsigned int attrsz; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + /* + * First check group, which is the last field in all descriptors + * we build, and in most descriptors built by Windows + */ + attrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + offgroup = le32_to_cpu(phead->group); + if (offgroup >= attrsz) { + /* find end of GSID */ + psid = (const SID*)&attr[offgroup]; + endsid = offgroup + ntfs_sid_size(psid); + if (endsid > attrsz) attrsz = endsid; + } + offowner = le32_to_cpu(phead->owner); + if (offowner >= attrsz) { + /* find end of USID */ + psid = (const SID*)&attr[offowner]; + endsid = offowner + ntfs_sid_size(psid); + attrsz = endsid; + } + offsacl = le32_to_cpu(phead->sacl); + if (offsacl >= attrsz) { + /* find end of SACL */ + psacl = (const ACL*)&attr[offsacl]; + endacl = offsacl + le16_to_cpu(psacl->size); + if (endacl > attrsz) + attrsz = endacl; + } + + + /* find end of DACL */ + offdacl = le32_to_cpu(phead->dacl); + if (offdacl >= attrsz) { + pdacl = (const ACL*)&attr[offdacl]; + endacl = offdacl + le16_to_cpu(pdacl->size); + if (endacl > attrsz) + attrsz = endacl; + } + return (attrsz); +} + +/* + * Do sanity checks on a SID read from storage + * (just check revision and number of authorities) + */ + +BOOL ntfs_valid_sid(const SID *sid) +{ + return ((sid->revision == SID_REVISION) + && (sid->sub_authority_count >= 1) + && (sid->sub_authority_count <= 8)); +} + +/* + * Check whether a SID is acceptable for an implicit + * mapping pattern. + * It should have been already checked it is a valid user SID. + * + * The last authority reference has to be >= 1000 (Windows usage) + * and <= 0x7fffffff, so that 30 bits from a uid and 30 more bits + * from a gid an be inserted with no overflow. + */ + +BOOL ntfs_valid_pattern(const SID *sid) +{ + int cnt; + u32 auth; + le32 leauth; + + cnt = sid->sub_authority_count; + leauth = sid->sub_authority[cnt-1]; + auth = le32_to_cpu(leauth); + return ((auth >= 1000) && (auth <= 0x7fffffff)); +} + +/* + * Compute the uid or gid associated to a SID + * through an implicit mapping + * + * Returns 0 (root) if it does not match pattern + */ + +static u32 findimplicit(const SID *xsid, const SID *pattern, int parity) +{ + BIGSID defsid; + SID *psid; + u32 xid; /* uid or gid */ + int cnt; + u32 carry; + le32 leauth; + u32 uauth; + u32 xlast; + u32 rlast; + + memcpy(&defsid,pattern,ntfs_sid_size(pattern)); + psid = (SID*)&defsid; + cnt = psid->sub_authority_count; + xid = 0; + if (xsid->sub_authority_count == cnt) { + psid->sub_authority[cnt-1] = xsid->sub_authority[cnt-1]; + leauth = xsid->sub_authority[cnt-1]; + xlast = le32_to_cpu(leauth); + leauth = pattern->sub_authority[cnt-1]; + rlast = le32_to_cpu(leauth); + + if ((xlast > rlast) && !((xlast ^ rlast ^ parity) & 1)) { + /* direct check for basic situation */ + if (ntfs_same_sid(psid,xsid)) + xid = ((xlast - rlast) >> 1) & 0x3fffffff; + else { + /* + * check whether part of mapping had to be + * recorded in a higher level authority + */ + carry = 1; + do { + leauth = psid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + 1; + psid->sub_authority[cnt-2] + = cpu_to_le32(uauth); + } while (!ntfs_same_sid(psid,xsid) + && (++carry < 4)); + if (carry < 4) + xid = (((xlast - rlast) >> 1) + & 0x3fffffff) | (carry << 30); + } + } + } + return (xid); +} + +/* + * Find usid mapped to a Linux user + * Returns NULL if not found + */ + +const SID *ntfs_find_usid(const struct MAPPING* usermapping, + uid_t uid, SID *defusid) +{ + const struct MAPPING *p; + const SID *sid; + le32 leauth; + u32 uauth; + int cnt; + + if (!uid) + sid = adminsid; + else { + p = usermapping; + while (p && p->xid && ((uid_t)p->xid != uid)) + p = p->next; + if (p && !p->xid) { + /* + * default pattern has been reached : + * build an implicit SID according to pattern + * (the pattern format was checked while reading + * the mapping file) + */ + memcpy(defusid, p->sid, ntfs_sid_size(p->sid)); + cnt = defusid->sub_authority_count; + leauth = defusid->sub_authority[cnt-1]; + uauth = le32_to_cpu(leauth) + 2*(uid & 0x3fffffff); + defusid->sub_authority[cnt-1] = cpu_to_le32(uauth); + if (uid & 0xc0000000) { + leauth = defusid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + ((uid >> 30) & 3); + defusid->sub_authority[cnt-2] = cpu_to_le32(uauth); + } + sid = defusid; + } else + sid = (p ? p->sid : (const SID*)NULL); + } + return (sid); +} + +/* + * Find Linux group mapped to a gsid + * Returns 0 (root) if not found + */ + +const SID *ntfs_find_gsid(const struct MAPPING* groupmapping, + gid_t gid, SID *defgsid) +{ + const struct MAPPING *p; + const SID *sid; + le32 leauth; + u32 uauth; + int cnt; + + if (!gid) + sid = adminsid; + else { + p = groupmapping; + while (p && p->xid && ((gid_t)p->xid != gid)) + p = p->next; + if (p && !p->xid) { + /* + * default pattern has been reached : + * build an implicit SID according to pattern + * (the pattern format was checked while reading + * the mapping file) + */ + memcpy(defgsid, p->sid, ntfs_sid_size(p->sid)); + cnt = defgsid->sub_authority_count; + leauth = defgsid->sub_authority[cnt-1]; + uauth = le32_to_cpu(leauth) + 2*(gid & 0x3fffffff) + 1; + defgsid->sub_authority[cnt-1] = cpu_to_le32(uauth); + if (gid & 0xc0000000) { + leauth = defgsid->sub_authority[cnt-2]; + uauth = le32_to_cpu(leauth) + ((gid >> 30) & 3); + defgsid->sub_authority[cnt-2] = cpu_to_le32(uauth); + } + sid = defgsid; + } else + sid = (p ? p->sid : (const SID*)NULL); + } + return (sid); +} + +/* + * Find Linux owner mapped to a usid + * Returns 0 (root) if not found + */ + +uid_t ntfs_find_user(const struct MAPPING* usermapping, const SID *usid) +{ + uid_t uid; + const struct MAPPING *p; + + p = usermapping; + while (p && p->xid && !ntfs_same_sid(usid, p->sid)) + p = p->next; + if (p && !p->xid) + /* + * No explicit mapping found, try implicit mapping + */ + uid = findimplicit(usid,p->sid,0); + else + uid = (p ? p->xid : 0); + return (uid); +} + +/* + * Find Linux group mapped to a gsid + * Returns 0 (root) if not found + */ + +gid_t ntfs_find_group(const struct MAPPING* groupmapping, const SID * gsid) +{ + gid_t gid; + const struct MAPPING *p; + + p = groupmapping; + while (p && p->xid && !ntfs_same_sid(gsid, p->sid)) + p = p->next; + if (p && !p->xid) + /* + * No explicit mapping found, try implicit mapping + */ + gid = findimplicit(gsid,p->sid,1); + else + gid = (p ? p->xid : 0); + return (gid); +} + +/* + * Check the validity of the ACEs in a DACL or SACL + */ + +static BOOL valid_acl(const ACL *pacl, unsigned int end) +{ + const ACCESS_ALLOWED_ACE *pace; + unsigned int offace; + unsigned int acecnt; + unsigned int acesz; + unsigned int nace; + BOOL ok; + + ok = TRUE; + acecnt = le16_to_cpu(pacl->ace_count); + offace = sizeof(ACL); + for (nace = 0; (nace < acecnt) && ok; nace++) { + /* be sure the beginning is within range */ + if ((offace + sizeof(ACCESS_ALLOWED_ACE)) > end) + ok = FALSE; + else { + pace = (const ACCESS_ALLOWED_ACE*) + &((const char*)pacl)[offace]; + acesz = le16_to_cpu(pace->size); + if (((offace + acesz) > end) + || !ntfs_valid_sid(&pace->sid) + || ((ntfs_sid_size(&pace->sid) + 8) != (int)acesz)) + ok = FALSE; + offace += acesz; + } + } + return (ok); +} + +/* + * Do sanity checks on security descriptors read from storage + * basically, we make sure that every field holds within + * allocated storage + * Should not be called with a NULL argument + * returns TRUE if considered safe + * if not, error should be logged by caller + */ + +BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pdacl; + const ACL *psacl; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + BOOL ok; + + ok = TRUE; + + /* + * first check overall size if within allocation range + * and a DACL is present + * and owner and group SID are valid + */ + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + offsacl = le32_to_cpu(phead->sacl); + offowner = le32_to_cpu(phead->owner); + offgroup = le32_to_cpu(phead->group); + pdacl = (const ACL*)&securattr[offdacl]; + psacl = (const ACL*)&securattr[offsacl]; + + /* + * size check occurs before the above pointers are used + * + * "DR Watson" standard directory on WinXP has an + * old revision and no DACL though SE_DACL_PRESENT is set + */ + if ((attrsz >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (phead->revision == SECURITY_DESCRIPTOR_REVISION) + && (offowner >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && ((offowner + 2) < attrsz) + && (offgroup >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && ((offgroup + 2) < attrsz) + && (!offdacl + || ((offdacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (offdacl+sizeof(ACL) <= attrsz))) + && (!offsacl + || ((offsacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) + && (offsacl+sizeof(ACL) <= attrsz))) + && !(phead->owner & const_cpu_to_le32(3)) + && !(phead->group & const_cpu_to_le32(3)) + && !(phead->dacl & const_cpu_to_le32(3)) + && !(phead->sacl & const_cpu_to_le32(3)) + && (ntfs_attr_size(securattr) <= attrsz) + && ntfs_valid_sid((const SID*)&securattr[offowner]) + && ntfs_valid_sid((const SID*)&securattr[offgroup]) + /* + * if there is an ACL, as indicated by offdacl, + * require SE_DACL_PRESENT + * but "Dr Watson" has SE_DACL_PRESENT though no DACL + */ + && (!offdacl + || ((phead->control & SE_DACL_PRESENT) + && ((pdacl->revision == ACL_REVISION) + || (pdacl->revision == ACL_REVISION_DS)))) + /* same for SACL */ + && (!offsacl + || ((phead->control & SE_SACL_PRESENT) + && ((psacl->revision == ACL_REVISION) + || (psacl->revision == ACL_REVISION_DS))))) { + /* + * Check the DACL and SACL if present + */ + if ((offdacl && !valid_acl(pdacl,attrsz - offdacl)) + || (offsacl && !valid_acl(psacl,attrsz - offsacl))) + ok = FALSE; + } else + ok = FALSE; + return (ok); +} + +/* + * Copy the inheritable parts of an ACL + * + * Returns the size of the new ACL + * or zero if nothing is inheritable + */ + +int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, + const SID *usid, const SID *gsid, BOOL fordir, + le16 inherited) +{ + unsigned int src; + unsigned int dst; + int oldcnt; + int newcnt; + unsigned int selection; + int nace; + int acesz; + int usidsz; + int gsidsz; + const ACCESS_ALLOWED_ACE *poldace; + ACCESS_ALLOWED_ACE *pnewace; + ACCESS_ALLOWED_ACE *pauthace; + ACCESS_ALLOWED_ACE *pownerace; + + pauthace = (ACCESS_ALLOWED_ACE*)NULL; + pownerace = (ACCESS_ALLOWED_ACE*)NULL; + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + + /* ACL header */ + + newacl->revision = ACL_REVISION; + newacl->alignment1 = 0; + newacl->alignment2 = const_cpu_to_le16(0); + src = dst = sizeof(ACL); + + selection = (fordir ? CONTAINER_INHERIT_ACE : OBJECT_INHERIT_ACE); + newcnt = 0; + oldcnt = le16_to_cpu(oldacl->ace_count); + for (nace = 0; nace < oldcnt; nace++) { + poldace = (const ACCESS_ALLOWED_ACE*)((const char*)oldacl + src); + acesz = le16_to_cpu(poldace->size); + src += acesz; + /* + * Extract inheritance for access, including inheritance for + * access from an ACE with is both applied and inheritable. + * + * must not output OBJECT_INHERIT_ACE or CONTAINER_INHERIT_ACE + * + * According to MSDN : + * "For a case in which a container object inherits an ACE + * "that is both effective on the container and inheritable + * "by its descendants, the container may inherit two ACEs. + * "This occurs if the inheritable ACE contains generic + * "information." + */ + if ((poldace->flags & selection) + && (!fordir + || (poldace->flags & NO_PROPAGATE_INHERIT_ACE) + || (poldace->mask & (GENERIC_ALL | GENERIC_READ + | GENERIC_WRITE | GENERIC_EXECUTE))) + && !ntfs_same_sid(&poldace->sid, ownersid) + && !ntfs_same_sid(&poldace->sid, groupsid)) { + pnewace = (ACCESS_ALLOWED_ACE*) + ((char*)newacl + dst); + memcpy(pnewace,poldace,acesz); + /* reencode GENERIC_ALL */ + if (pnewace->mask & GENERIC_ALL) { + pnewace->mask &= ~GENERIC_ALL; + if (fordir) + pnewace->mask |= OWNER_RIGHTS + | DIR_READ + | DIR_WRITE + | DIR_EXEC; + else + /* + * The last flag is not defined for a file, + * however Windows sets it, so do the same + */ + pnewace->mask |= OWNER_RIGHTS + | FILE_READ + | FILE_WRITE + | FILE_EXEC + | cpu_to_le32(0x40); + } + /* reencode GENERIC_READ (+ EXECUTE) */ + if (pnewace->mask & GENERIC_READ) { + if (fordir) + pnewace->mask |= OWNER_RIGHTS + | DIR_READ + | DIR_EXEC; + else + pnewace->mask |= OWNER_RIGHTS + | FILE_READ + | FILE_EXEC; + pnewace->mask &= ~(GENERIC_READ + | GENERIC_EXECUTE + | WRITE_DAC + | WRITE_OWNER + | DELETE | FILE_WRITE_EA + | FILE_WRITE_ATTRIBUTES); + } + /* reencode GENERIC_WRITE */ + if (pnewace->mask & GENERIC_WRITE) { + if (fordir) + pnewace->mask |= OWNER_RIGHTS + | DIR_WRITE; + else + pnewace->mask |= OWNER_RIGHTS + | FILE_WRITE; + pnewace->mask &= ~(GENERIC_WRITE + | WRITE_DAC + | WRITE_OWNER + | FILE_DELETE_CHILD); + } + /* remove inheritance flags */ + pnewace->flags &= ~(OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE + | INHERIT_ONLY_ACE); + /* + * Group similar ACE for authenticated users + * (should probably be done for other SIDs) + */ + if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE) + && ntfs_same_sid(&poldace->sid, authsid)) { + if (pauthace) { + pauthace->flags |= pnewace->flags; + pauthace->mask |= pnewace->mask; + } else { + pauthace = pnewace; + if (inherited) + pnewace->flags |= INHERITED_ACE; + dst += acesz; + newcnt++; + } + } else { + if (inherited) + pnewace->flags |= INHERITED_ACE; + dst += acesz; + newcnt++; + } + } + /* + * Inheritance for access, specific to + * creator-owner (and creator-group) + */ + if (fordir || !inherited + || (poldace->flags + & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) { + pnewace = (ACCESS_ALLOWED_ACE*) + ((char*)newacl + dst); + memcpy(pnewace,poldace,acesz); + /* + * Replace generic creator-owner and + * creator-group by owner and group + * (but keep for further inheritance) + */ + if (ntfs_same_sid(&pnewace->sid, ownersid)) { + memcpy(&pnewace->sid, usid, usidsz); + pnewace->size = cpu_to_le16(usidsz + 8); + /* remove inheritance flags */ + pnewace->flags &= ~(OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE + | INHERIT_ONLY_ACE); + if (inherited) + pnewace->flags |= INHERITED_ACE; + if ((pnewace->type == ACCESS_ALLOWED_ACE_TYPE) + && pownerace + && !(pnewace->flags & ~pownerace->flags)) { + pownerace->mask |= pnewace->mask; + } else { + dst += usidsz + 8; + newcnt++; + } + } + if (ntfs_same_sid(&pnewace->sid, groupsid)) { + memcpy(&pnewace->sid, gsid, gsidsz); + pnewace->size = cpu_to_le16(gsidsz + 8); + /* remove inheritance flags */ + pnewace->flags &= ~(OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE + | INHERIT_ONLY_ACE); + if (inherited) + pnewace->flags |= INHERITED_ACE; + dst += gsidsz + 8; + newcnt++; + } + } + + /* + * inheritance for further inheritance + * + * Situations leading to output CONTAINER_INHERIT_ACE + * or OBJECT_INHERIT_ACE + */ + if (fordir + && (poldace->flags + & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) { + pnewace = (ACCESS_ALLOWED_ACE*) + ((char*)newacl + dst); + memcpy(pnewace,poldace,acesz); + if ((poldace->flags & OBJECT_INHERIT_ACE) + && !(poldace->flags & (CONTAINER_INHERIT_ACE + | NO_PROPAGATE_INHERIT_ACE))) + pnewace->flags |= INHERIT_ONLY_ACE; + if ((poldace->flags & CONTAINER_INHERIT_ACE) + && !(poldace->flags & NO_PROPAGATE_INHERIT_ACE) + && !ntfs_same_sid(&poldace->sid, ownersid) + && !ntfs_same_sid(&poldace->sid, groupsid)) { + if ((poldace->mask & (GENERIC_ALL | GENERIC_READ + | GENERIC_WRITE | GENERIC_EXECUTE))) + pnewace->flags |= INHERIT_ONLY_ACE; + else + pnewace->flags &= ~INHERIT_ONLY_ACE; + } + if (inherited) + pnewace->flags |= INHERITED_ACE; + /* + * Prepare grouping similar ACE for authenticated users + */ + if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE) + && !pauthace + && !(pnewace->flags & INHERIT_ONLY_ACE) + && ntfs_same_sid(&poldace->sid, authsid)) { + pauthace = pnewace; + } + /* + * Prepare grouping similar ACE for owner + */ + if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE) + && !pownerace + && !(pnewace->flags & INHERIT_ONLY_ACE) + && ntfs_same_sid(&poldace->sid, usid)) { + pownerace = pnewace; + } + dst += acesz; + newcnt++; + } + } + /* + * Adjust header if something was inherited + */ + if (dst > sizeof(ACL)) { + newacl->ace_count = cpu_to_le16(newcnt); + newacl->size = cpu_to_le16(dst); + } else + dst = 0; + return (dst); +} + +#if POSIXACLS + +/* + * Do sanity checks on a Posix descriptor + * Should not be called with a NULL argument + * returns TRUE if considered safe + * if not, error should be logged by caller + */ + +BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc) +{ + const struct POSIX_ACL *pacl; + int i; + BOOL ok; + u16 tag; + u32 id; + int perms; + struct { + u16 previous; + u32 previousid; + u16 tagsset; + mode_t mode; + int owners; + int groups; + int others; + } checks[2], *pchk; + + for (i=0; i<2; i++) { + checks[i].mode = 0; + checks[i].tagsset = 0; + checks[i].owners = 0; + checks[i].groups = 0; + checks[i].others = 0; + checks[i].previous = 0; + checks[i].previousid = 0; + } + ok = TRUE; + pacl = &pxdesc->acl; + /* + * header (strict for now) + */ + if ((pacl->version != POSIX_VERSION) + || (pacl->flags != 0) + || (pacl->filler != 0)) + ok = FALSE; + /* + * Reject multiple owner, group or other + * but do not require them to be present + * Also check the ACEs are in correct order + * which implies there is no duplicates + */ + for (i=0; i<pxdesc->acccnt + pxdesc->defcnt; i++) { + if (i >= pxdesc->firstdef) + pchk = &checks[1]; + else + pchk = &checks[0]; + perms = pacl->ace[i].perms; + tag = pacl->ace[i].tag; + pchk->tagsset |= tag; + id = pacl->ace[i].id; + if (perms & ~7) ok = FALSE; + if ((tag < pchk->previous) + || ((tag == pchk->previous) + && (id <= pchk->previousid))) + ok = FALSE; + pchk->previous = tag; + pchk->previousid = id; + switch (tag) { + case POSIX_ACL_USER_OBJ : + if (pchk->owners++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode |= perms << 6; + break; + case POSIX_ACL_GROUP_OBJ : + if (pchk->groups++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode = (pchk->mode & 07707) | (perms << 3); + break; + case POSIX_ACL_OTHER : + if (pchk->others++) + ok = FALSE; + if (id != (u32)-1) + ok = FALSE; + pchk->mode |= perms; + break; + case POSIX_ACL_USER : + case POSIX_ACL_GROUP : + if (id == (u32)-1) + ok = FALSE; + break; + case POSIX_ACL_MASK : + if (id != (u32)-1) + ok = FALSE; + pchk->mode = (pchk->mode & 07707) | (perms << 3); + break; + default : + ok = FALSE; + break; + } + } + if ((pxdesc->acccnt > 0) + && ((checks[0].owners != 1) || (checks[0].groups != 1) + || (checks[0].others != 1))) + ok = FALSE; + /* do not check owner, group or other are present in */ + /* the default ACL, Windows does not necessarily set them */ + /* descriptor */ + if (pxdesc->defcnt && (pxdesc->acccnt > pxdesc->firstdef)) + ok = FALSE; + if ((pxdesc->acccnt < 0) || (pxdesc->defcnt < 0)) + ok = FALSE; + /* check mode, unless null or no tag set */ + if (pxdesc->mode + && checks[0].tagsset + && (checks[0].mode != (pxdesc->mode & 0777))) + ok = FALSE; + /* check tagsset */ + if (pxdesc->tagsset != checks[0].tagsset) + ok = FALSE; + return (ok); +} + +/* + * Set standard header data into a Posix ACL + * The mode argument should provide the 3 upper bits of target mode + */ + +static mode_t posix_header(struct POSIX_SECURITY *pxdesc, mode_t basemode) +{ + mode_t mode; + u16 tagsset; + struct POSIX_ACE *pace; + int i; + + mode = basemode & 07000; + tagsset = 0; + for (i=0; i<pxdesc->acccnt; i++) { + pace = &pxdesc->acl.ace[i]; + tagsset |= pace->tag; + switch(pace->tag) { + case POSIX_ACL_USER_OBJ : + mode |= (pace->perms & 7) << 6; + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((pace->perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= pace->perms & 7; + break; + default : + break; + } + } + pxdesc->tagsset = tagsset; + pxdesc->mode = mode; + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + return (mode); +} + +/* + * Sort ACEs in a Posix ACL + * This is useful for always getting reusable converted ACLs, + * it also helps in merging ACEs. + * Repeated tag+id are allowed and not merged here. + * + * Tags should be in ascending sequence and for a repeatable tag + * ids should be in ascending sequence. + */ + +void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc) +{ + struct POSIX_ACL *pacl; + struct POSIX_ACE ace; + int i; + int offs; + BOOL done; + u16 tag; + u16 previous; + u32 id; + u32 previousid; + + + /* + * Check sequencing of tag+id in access ACE's + */ + pacl = &pxdesc->acl; + do { + done = TRUE; + previous = pacl->ace[0].tag; + previousid = pacl->ace[0].id; + for (i=1; i<pxdesc->acccnt; i++) { + tag = pacl->ace[i].tag; + id = pacl->ace[i].id; + + if ((tag < previous) + || ((tag == previous) && (id < previousid))) { + done = FALSE; + memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE)); + } else { + previous = tag; + previousid = id; + } + } + } while (!done); + /* + * Same for default ACEs + */ + do { + done = TRUE; + if ((pxdesc->defcnt) > 1) { + offs = pxdesc->firstdef; + previous = pacl->ace[offs].tag; + previousid = pacl->ace[offs].id; + for (i=offs+1; i<offs+pxdesc->defcnt; i++) { + tag = pacl->ace[i].tag; + id = pacl->ace[i].id; + + if ((tag < previous) + || ((tag == previous) && (id < previousid))) { + done = FALSE; + memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE)); + memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE)); + } else { + previous = tag; + previousid = id; + } + } + } + } while (!done); +} + +/* + * Merge a new mode into a Posix descriptor + * The Posix descriptor is not reallocated, its size is unchanged + * + * returns 0 if ok + */ + +int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode) +{ + int i; + BOOL maskfound; + struct POSIX_ACE *pace; + int todo; + + maskfound = FALSE; + todo = POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER; + for (i=pxdesc->acccnt-1; i>=0; i--) { + pace = &pxdesc->acl.ace[i]; + switch(pace->tag) { + case POSIX_ACL_USER_OBJ : + pace->perms = (mode >> 6) & 7; + todo &= ~POSIX_ACL_USER_OBJ; + break; + case POSIX_ACL_GROUP_OBJ : + if (!maskfound) + pace->perms = (mode >> 3) & 7; + todo &= ~POSIX_ACL_GROUP_OBJ; + break; + case POSIX_ACL_MASK : + pace->perms = (mode >> 3) & 7; + maskfound = TRUE; + break; + case POSIX_ACL_OTHER : + pace->perms = mode & 7; + todo &= ~POSIX_ACL_OTHER; + break; + default : + break; + } + } + pxdesc->mode = mode; + return (todo ? -1 : 0); +} + +/* + * Replace an access or default Posix ACL + * The resulting ACL is checked for validity + * + * Returns a new ACL or NULL if there is a problem + */ + +struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc, + const struct POSIX_ACL *newacl, int count, BOOL deflt) +{ + struct POSIX_SECURITY *newpxdesc; + size_t newsize; + int offset; + int oldoffset; + int i; + + if (deflt) + newsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + count)*sizeof(struct POSIX_ACE); + else + newsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->defcnt + count)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(newsize); + if (newpxdesc) { + if (deflt) { + offset = oldpxdesc->acccnt; + newpxdesc->acccnt = oldpxdesc->acccnt; + newpxdesc->defcnt = count; + newpxdesc->firstdef = offset; + /* copy access ACEs */ + for (i=0; i<newpxdesc->acccnt; i++) + newpxdesc->acl.ace[i] = oldpxdesc->acl.ace[i]; + /* copy default ACEs */ + for (i=0; i<count; i++) + newpxdesc->acl.ace[i + offset] = newacl->ace[i]; + } else { + offset = count; + newpxdesc->acccnt = count; + newpxdesc->defcnt = oldpxdesc->defcnt; + newpxdesc->firstdef = count; + /* copy access ACEs */ + for (i=0; i<count; i++) + newpxdesc->acl.ace[i] = newacl->ace[i]; + /* copy default ACEs */ + oldoffset = oldpxdesc->firstdef; + for (i=0; i<newpxdesc->defcnt; i++) + newpxdesc->acl.ace[i + offset] = oldpxdesc->acl.ace[i + oldoffset]; + } + /* assume special flags unchanged */ + posix_header(newpxdesc, oldpxdesc->mode); + if (!ntfs_valid_posix(newpxdesc)) { + /* do not log, this is an application error */ + free(newpxdesc); + newpxdesc = (struct POSIX_SECURITY*)NULL; + errno = EINVAL; + } + } else + errno = ENOMEM; + return (newpxdesc); +} + +/* + * Build a basic Posix ACL from a mode and umask, + * ignoring inheritance from the parent directory + */ + +struct POSIX_SECURITY *ntfs_build_basic_posix( + const struct POSIX_SECURITY *pxdesc __attribute__((unused)), + mode_t mode, mode_t mask, BOOL isdir __attribute__((unused))) +{ + struct POSIX_SECURITY *pydesc; + struct POSIX_ACE *pyace; + + pydesc = (struct POSIX_SECURITY*)malloc( + sizeof(struct POSIX_SECURITY) + 3*sizeof(struct POSIX_ACE)); + if (pydesc) { + pyace = &pydesc->acl.ace[0]; + pyace->tag = POSIX_ACL_USER_OBJ; + pyace->perms = ((mode & ~mask) >> 6) & 7; + pyace->id = -1; + pyace = &pydesc->acl.ace[1]; + pyace->tag = POSIX_ACL_GROUP_OBJ; + pyace->perms = ((mode & ~mask) >> 3) & 7; + pyace->id = -1; + pyace = &pydesc->acl.ace[2]; + pyace->tag = POSIX_ACL_OTHER; + pyace->perms = (mode & ~mask) & 7; + pyace->id = -1; + pydesc->mode = mode; + pydesc->tagsset = POSIX_ACL_USER_OBJ + | POSIX_ACL_GROUP_OBJ + | POSIX_ACL_OTHER; + pydesc->acccnt = 3; + pydesc->defcnt = 0; + pydesc->firstdef = 6; + } else + errno = ENOMEM; + return (pydesc); +} + +/* + * Build an inherited Posix descriptor from parent + * descriptor (if any) restricted to creation mode + * + * Returns the inherited descriptor or NULL if there is a problem + */ + +struct POSIX_SECURITY *ntfs_build_inherited_posix( + const struct POSIX_SECURITY *pxdesc, mode_t mode, + mode_t mask, BOOL isdir) +{ + struct POSIX_SECURITY *pydesc; + struct POSIX_ACE *pyace; + int count; + int defcnt; + int size; + int i; + s16 tagsset; + + if (pxdesc && pxdesc->defcnt) { + if (isdir) + count = 2*pxdesc->defcnt + 3; + else + count = pxdesc->defcnt + 3; + } else + count = 3; + pydesc = (struct POSIX_SECURITY*)malloc( + sizeof(struct POSIX_SECURITY) + count*sizeof(struct POSIX_ACE)); + if (pydesc) { + /* + * Copy inherited tags and adapt perms + * Use requested mode, ignoring umask + * (not possible with older versions of fuse) + */ + tagsset = 0; + defcnt = (pxdesc ? pxdesc->defcnt : 0); + for (i=defcnt-1; i>=0; i--) { + pyace = &pydesc->acl.ace[i]; + *pyace = pxdesc->acl.ace[pxdesc->firstdef + i]; + switch (pyace->tag) { + case POSIX_ACL_USER_OBJ : + pyace->perms &= (mode >> 6) & 7; + break; + case POSIX_ACL_GROUP_OBJ : + if (!(tagsset & POSIX_ACL_MASK)) + pyace->perms &= (mode >> 3) & 7; + break; + case POSIX_ACL_OTHER : + pyace->perms &= mode & 7; + break; + case POSIX_ACL_MASK : + pyace->perms &= (mode >> 3) & 7; + break; + default : + break; + } + tagsset |= pyace->tag; + } + pydesc->acccnt = defcnt; + /* + * If some standard tags were missing, append them from mode + * and sort the list + * Here we have to use the umask'ed mode + */ + if (~tagsset & (POSIX_ACL_USER_OBJ + | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER)) { + i = defcnt; + /* owner was missing */ + if (!(tagsset & POSIX_ACL_USER_OBJ)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_USER_OBJ; + pyace->id = -1; + pyace->perms = ((mode & ~mask) >> 6) & 7; + tagsset |= POSIX_ACL_USER_OBJ; + i++; + } + /* owning group was missing */ + if (!(tagsset & POSIX_ACL_GROUP_OBJ)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_GROUP_OBJ; + pyace->id = -1; + pyace->perms = ((mode & ~mask) >> 3) & 7; + tagsset |= POSIX_ACL_GROUP_OBJ; + i++; + } + /* other was missing */ + if (!(tagsset & POSIX_ACL_OTHER)) { + pyace = &pydesc->acl.ace[i]; + pyace->tag = POSIX_ACL_OTHER; + pyace->id = -1; + pyace->perms = mode & ~mask & 7; + tagsset |= POSIX_ACL_OTHER; + i++; + } + pydesc->acccnt = i; + pydesc->firstdef = i; + pydesc->defcnt = 0; + ntfs_sort_posix(pydesc); + } + + /* + * append as a default ACL if a directory + */ + pydesc->firstdef = pydesc->acccnt; + if (defcnt && isdir) { + size = sizeof(struct POSIX_ACE)*defcnt; + memcpy(&pydesc->acl.ace[pydesc->firstdef], + &pxdesc->acl.ace[pxdesc->firstdef],size); + pydesc->defcnt = defcnt; + } else { + pydesc->defcnt = 0; + } + /* assume special bits are not inherited */ + posix_header(pydesc, mode & 07000); + if (!ntfs_valid_posix(pydesc)) { + ntfs_log_error("Error building an inherited Posix desc\n"); + errno = EIO; + free(pydesc); + pydesc = (struct POSIX_SECURITY*)NULL; + } + } else + errno = ENOMEM; + return (pydesc); +} + +static int merge_lists_posix(struct POSIX_ACE *targetace, + const struct POSIX_ACE *firstace, + const struct POSIX_ACE *secondace, + int firstcnt, int secondcnt) +{ + int k; + + k = 0; + /* + * No list is exhausted : + * if same tag+id in both list : + * ignore ACE from second list + * else take the one with smaller tag+id + */ + while ((firstcnt > 0) && (secondcnt > 0)) + if ((firstace->tag == secondace->tag) + && (firstace->id == secondace->id)) { + secondace++; + secondcnt--; + } else + if ((firstace->tag < secondace->tag) + || ((firstace->tag == secondace->tag) + && (firstace->id < secondace->id))) { + targetace->tag = firstace->tag; + targetace->id = firstace->id; + targetace->perms = firstace->perms; + firstace++; + targetace++; + firstcnt--; + k++; + } else { + targetace->tag = secondace->tag; + targetace->id = secondace->id; + targetace->perms = secondace->perms; + secondace++; + targetace++; + secondcnt--; + k++; + } + /* + * One list is exhausted, copy the other one + */ + while (firstcnt > 0) { + targetace->tag = firstace->tag; + targetace->id = firstace->id; + targetace->perms = firstace->perms; + firstace++; + targetace++; + firstcnt--; + k++; + } + while (secondcnt > 0) { + targetace->tag = secondace->tag; + targetace->id = secondace->id; + targetace->perms = secondace->perms; + secondace++; + targetace++; + secondcnt--; + k++; + } + return (k); +} + +/* + * Merge two Posix ACLs + * The input ACLs have to be adequately sorted + * + * Returns the merged ACL, which is allocated and has to be freed by caller, + * or NULL if failed + */ + +struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first, + const struct POSIX_SECURITY *second) +{ + struct POSIX_SECURITY *pxdesc; + struct POSIX_ACE *targetace; + const struct POSIX_ACE *firstace; + const struct POSIX_ACE *secondace; + size_t size; + int k; + + size = sizeof(struct POSIX_SECURITY) + + (first->acccnt + first->defcnt + + second->acccnt + second->defcnt)*sizeof(struct POSIX_ACE); + pxdesc = (struct POSIX_SECURITY*)malloc(size); + if (pxdesc) { + /* + * merge access ACEs + */ + firstace = first->acl.ace; + secondace = second->acl.ace; + targetace = pxdesc->acl.ace; + k = merge_lists_posix(targetace,firstace,secondace, + first->acccnt,second->acccnt); + pxdesc->acccnt = k; + /* + * merge default ACEs + */ + pxdesc->firstdef = k; + firstace = &first->acl.ace[first->firstdef]; + secondace = &second->acl.ace[second->firstdef]; + targetace = &pxdesc->acl.ace[k]; + k = merge_lists_posix(targetace,firstace,secondace, + first->defcnt,second->defcnt); + pxdesc->defcnt = k; + /* + * build header + */ + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + pxdesc->mode = 0; + pxdesc->tagsset = 0; + } else + errno = ENOMEM; + return (pxdesc); +} + +struct BUILD_CONTEXT { + BOOL isdir; + BOOL adminowns; + BOOL groupowns; + u16 selfuserperms; + u16 selfgrpperms; + u16 grpperms; + u16 othperms; + u16 mask; + u16 designates; + u16 withmask; + u16 rootspecial; +} ; + + + +static BOOL build_user_denials(ACL *pacl, + const SID *usid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) +{ + BIGSID defsid; + ACCESS_ALLOWED_ACE *pdace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + le32 denials; + u16 perms; + u16 mixperms; + u16 tag; + BOOL rejected; + BOOL rootuser; + BOOL avoidmask; + + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + rootuser = FALSE; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X)) + && ((pset->designates && pset->withmask) + || (!pset->designates && !pset->withmask)); + if (tag == POSIX_ACL_USER_OBJ) { + sid = usid; + sidsz = ntfs_sid_size(sid); + grants = OWNER_RIGHTS; + } else { + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + grants = WORLD_RIGHTS; + } else { + sid = adminsid; + rootuser = TRUE; + grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK; + } + if (sid) { + sidsz = ntfs_sid_size(sid); + /* + * Insert denial of complement of mask for + * each designated user (except root) + * WRITE_OWNER is inserted so that + * the mask can be identified + */ + if (!avoidmask && !rootuser) { + denials = WRITE_OWNER; + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (pset->isdir) { + if (!(pset->mask & POSIX_PERM_X)) + denials |= DIR_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= DIR_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= DIR_READ; + } else { + if (!(pset->mask & POSIX_PERM_X)) + denials |= FILE_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= FILE_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= FILE_READ; + } + if (rootuser) + grants &= ~ROOT_OWNER_UNMARK; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } else + rejected = TRUE; + } + if (!rejected) { + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + + /* a possible ACE to deny owner what he/she would */ + /* induely get from administrator, group or world */ + /* unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (!pset->adminowns && !rootuser) { + if (!pset->groupowns) { + mixperms = pset->grpperms | pset->othperms; + if (tag == POSIX_ACL_USER_OBJ) + mixperms |= pset->selfuserperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + } else { + mixperms = ~pset->grpperms & pset->othperms; + if (tag == POSIX_ACL_USER_OBJ) + mixperms |= pset->selfuserperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + } + denials &= ~grants; + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } + } + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (!rejected); +} + +static BOOL build_user_grants(ACL *pacl, + const SID *usid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) +{ + BIGSID defsid; + ACCESS_ALLOWED_ACE *pgace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + u16 perms; + u16 tag; + BOOL rejected; + BOOL rootuser; + + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + rootuser = FALSE; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + if (tag == POSIX_ACL_USER_OBJ) { + sid = usid; + sidsz = ntfs_sid_size(sid); + grants = OWNER_RIGHTS; + } else { + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid) + sidsz = ntfs_sid_size(sid); + else + rejected = TRUE; + grants = WORLD_RIGHTS; + } else { + sid = adminsid; + sidsz = ntfs_sid_size(sid); + rootuser = TRUE; + grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK; + } + } + if (!rejected) { + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + if (rootuser) + grants &= ~ROOT_OWNER_UNMARK; + pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->size = cpu_to_le16(sidsz + 8); + pgace->flags = flags; + pgace->mask = grants; + memcpy((char*)&pgace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + } + return (!rejected); +} + + + /* a grant ACE for group */ + /* unless group-obj has the same rights as world */ + /* but present if group is owner or owner is administrator */ + /* this ACE will be inserted after denials for group */ + +static BOOL build_group_denials_grant(ACL *pacl, + const SID *gsid, struct MAPPING* const mapping[], + ACE_FLAGS flags, const struct POSIX_ACE *pxace, + struct BUILD_CONTEXT *pset) +{ + BIGSID defsid; + ACCESS_ALLOWED_ACE *pdace; + ACCESS_ALLOWED_ACE *pgace; + const SID *sid; + int sidsz; + int pos; + int acecnt; + le32 grants; + le32 denials; + u16 perms; + u16 mixperms; + u16 tag; + BOOL avoidmask; + BOOL rootgroup; + BOOL rejected; + + rejected = FALSE; + tag = pxace->tag; + perms = pxace->perms; + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + rootgroup = FALSE; + avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X)) + && ((pset->designates && pset->withmask) + || (!pset->designates && !pset->withmask)); + if (tag == POSIX_ACL_GROUP_OBJ) + sid = gsid; + else + if (pxace->id) + sid = NTFS_FIND_GSID(mapping[MAPGROUPS], + pxace->id, (SID*)&defsid); + else { + sid = adminsid; + rootgroup = TRUE; + } + if (sid) { + sidsz = ntfs_sid_size(sid); + /* + * Insert denial of complement of mask for + * each group + * WRITE_OWNER is inserted so that + * the mask can be identified + * Note : this mask may lead on Windows to + * deny rights to administrators belonging + * to some user group + */ + if ((!avoidmask && !rootgroup) + || (pset->rootspecial + && (tag == POSIX_ACL_GROUP_OBJ))) { + denials = WRITE_OWNER; + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (pset->isdir) { + if (!(pset->mask & POSIX_PERM_X)) + denials |= DIR_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= DIR_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= DIR_READ; + } else { + if (!(pset->mask & POSIX_PERM_X)) + denials |= FILE_EXEC; + if (!(pset->mask & POSIX_PERM_W)) + denials |= FILE_WRITE; + if (!(pset->mask & POSIX_PERM_R)) + denials |= FILE_READ; + } + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } else + rejected = TRUE; + if (!rejected + && (pset->adminowns + || pset->groupowns + || avoidmask + || rootgroup + || (perms != pset->othperms))) { + grants = WORLD_RIGHTS; + if (rootgroup) + grants &= ~ROOT_GROUP_UNMARK; + if (pset->isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + + /* a possible ACE to deny group what it would get from world */ + /* or administrator, unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + if (!pset->adminowns + && !pset->groupowns + && !rootgroup) { + mixperms = pset->othperms; + if (tag == POSIX_ACL_GROUP_OBJ) + mixperms |= pset->selfgrpperms; + if (pset->isdir) { + if (mixperms & POSIX_PERM_X) + denials |= DIR_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= DIR_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= DIR_READ; + } else { + if (mixperms & POSIX_PERM_X) + denials |= FILE_EXEC; + if (mixperms & POSIX_PERM_W) + denials |= FILE_WRITE; + if (mixperms & POSIX_PERM_R) + denials |= FILE_READ; + } + denials &= ~(grants | OWNER_RIGHTS); + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = flags; + pdace->size = cpu_to_le16(sidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } + + /* now insert grants to group if more than world */ + if (pset->adminowns + || pset->groupowns + || (avoidmask && (pset->designates || pset->withmask)) + || (perms & ~pset->othperms) + || (pset->rootspecial + && (tag == POSIX_ACL_GROUP_OBJ)) + || (tag == POSIX_ACL_GROUP)) { + if (rootgroup) + grants &= ~ROOT_GROUP_UNMARK; + pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(sidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, sid, sidsz); + pos += sidsz + 8; + acecnt++; + } + } + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (!rejected); +} + + +/* + * Build an ACL composed of several ACE's + * returns size of ACL or zero if failed + * + * Three schemes are defined : + * + * 1) if root is neither owner nor group up to 7 ACE's are set up : + * - denials to owner (preventing grants to world or group to apply) + * + mask denials to designated user (unless mask allows all) + * + denials to designated user + * - grants to owner (always present - first grant) + * + grants to designated user + * + mask denial to group (unless mask allows all) + * - denials to group (preventing grants to world to apply) + * - grants to group (unless group has no more than world rights) + * + mask denials to designated group (unless mask allows all) + * + grants to designated group + * + denials to designated group + * - grants to world (unless none) + * - full privileges to administrator, always present + * - full privileges to system, always present + * + * The same scheme is applied for Posix ACLs, with the mask represented + * as denials prepended to grants for designated users and groups + * + * This is inspired by an Internet Draft from Marius Aamodt Eriksen + * for mapping NFSv4 ACLs to Posix ACLs (draft-ietf-nfsv4-acl-mapping-00.txt) + * More recent versions of the draft (draft-ietf-nfsv4-acl-mapping-05.txt) + * are not followed, as they ignore the Posix mask and lead to + * loss of compatibility with Linux implementations on other fs. + * + * Note that denials to group are located after grants to owner. + * This only occurs in the unfrequent situation where world + * has more rights than group and cannot be avoided if owner and other + * have some common right which is denied to group (eg for mode 745 + * executing has to be denied to group, but not to owner or world). + * This rare situation is processed by Windows correctly, but + * Windows utilities may want to change the order, with a + * consequence of applying the group denials to the Windows owner. + * The interpretation on Linux is not affected by the order change. + * + * 2) if root is either owner or group, two problems arise : + * - granting full rights to administrator (as needed to transpose + * to Windows rights bypassing granting to root) would imply + * Linux permissions to always be seen as rwx, no matter the chmod + * - there is no different SID to separate an administrator owner + * from an administrator group. Hence Linux permissions for owner + * would always be similar to permissions to group. + * + * as a work-around, up to 5 ACE's are set up if owner or group : + * - grants to owner, always present at first position + * - grants to group, always present + * - grants to world, unless none + * - full privileges to administrator, always present + * - full privileges to system, always present + * + * On Windows, these ACE's are processed normally, though they + * are redundant (owner, group and administrator are the same, + * as a consequence any denials would damage administrator rights) + * but on Linux, privileges to administrator are ignored (they + * are not needed as root has always full privileges), and + * neither grants to group are applied to owner, nor grants to + * world are applied to owner or group. + * + * 3) finally a similar situation arises when group is owner (they + * have the same SID), but is not root. + * In this situation up to 6 ACE's are set up : + * + * - denials to owner (preventing grants to world to apply) + * - grants to owner (always present) + * - grants to group (unless groups has same rights as world) + * - grants to world (unless none) + * - full privileges to administrator, always present + * - full privileges to system, always present + * + * On Windows, these ACE's are processed normally, though they + * are redundant (as owner and group are the same), but this has + * no impact on administrator rights + * + * Special flags (S_ISVTX, S_ISGID, S_ISUID) : + * an extra null ACE is inserted to hold these flags, using + * the same conventions as cygwin. + * + */ + +static int buildacls_posix(struct MAPPING* const mapping[], + char *secattr, int offs, const struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid) +{ + struct BUILD_CONTEXT aceset[2], *pset; + BOOL adminowns; + BOOL groupowns; + ACL *pacl; + ACCESS_ALLOWED_ACE *pgace; + ACCESS_ALLOWED_ACE *pdace; + const struct POSIX_ACE *pxace; + BOOL ok; + mode_t mode; + u16 tag; + u16 perms; + ACE_FLAGS flags; + int pos; + int i; + int k; + BIGSID defsid; + const SID *sid; + int acecnt; + int usidsz; + int wsidsz; + int asidsz; + int ssidsz; + int nsidsz; + le32 grants; + + usidsz = ntfs_sid_size(usid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + mode = pxdesc->mode; + /* adminowns and groupowns are used for both lists */ + adminowns = ntfs_same_sid(usid, adminsid) + || ntfs_same_sid(gsid, adminsid); + groupowns = !adminowns && ntfs_same_sid(usid, gsid); + + ok = TRUE; + + /* ACL header */ + pacl = (ACL*)&secattr[offs]; + pacl->revision = ACL_REVISION; + pacl->alignment1 = 0; + pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8); + pacl->ace_count = const_cpu_to_le16(0); + pacl->alignment2 = const_cpu_to_le16(0); + + /* + * Determine what is allowed to some group or world + * to prevent designated users or other groups to get + * rights from groups or world + * Do the same if owner and group appear as designated + * user or group + * Also get global mask + */ + for (k=0; k<2; k++) { + pset = &aceset[k]; + pset->selfuserperms = 0; + pset->selfgrpperms = 0; + pset->grpperms = 0; + pset->othperms = 0; + pset->mask = (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + pset->designates = 0; + pset->withmask = 0; + pset->rootspecial = 0; + pset->adminowns = adminowns; + pset->groupowns = groupowns; + pset->isdir = isdir; + } + + for (i=pxdesc->acccnt+pxdesc->defcnt-1; i>=0; i--) { + if (i >= pxdesc->acccnt) { + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + switch (pxace->tag) { + case POSIX_ACL_USER : + pset->designates++; + if (pxace->id) { + sid = NTFS_FIND_USID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid && ntfs_same_sid(sid,usid)) + pset->selfuserperms |= pxace->perms; + } else + /* root as designated user is processed apart */ + pset->rootspecial = TRUE; + break; + case POSIX_ACL_GROUP : + pset->designates++; + if (pxace->id) { + sid = NTFS_FIND_GSID(mapping[MAPUSERS], + pxace->id, (SID*)&defsid); + if (sid && ntfs_same_sid(sid,gsid)) + pset->selfgrpperms |= pxace->perms; + } else + /* root as designated group is processed apart */ + pset->rootspecial = TRUE; + /* fall through */ + case POSIX_ACL_GROUP_OBJ : + pset->grpperms |= pxace->perms; + break; + case POSIX_ACL_OTHER : + pset->othperms = pxace->perms; + break; + case POSIX_ACL_MASK : + pset->withmask++; + pset->mask = pxace->perms; + default : + break; + } + } + +if (pxdesc->defcnt && (pxdesc->firstdef != pxdesc->acccnt)) { +ntfs_log_error("** error : access and default not consecutive\n"); +return (0); +} + /* + * First insert all denials for owner and each + * designated user (with mask if needed) + */ + + pacl->ace_count = const_cpu_to_le16(0); + pacl->size = const_cpu_to_le16(sizeof(ACL)); + for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) { + if (i >= pxdesc->acccnt) { + flags = INHERIT_ONLY_ACE + | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + if (pxdesc->defcnt) + flags = NO_PROPAGATE_INHERIT_ACE; + else + flags = (isdir ? DIR_INHERITANCE + : FILE_INHERITANCE); + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + tag = pxace->tag; + perms = pxace->perms; + switch (tag) { + + /* insert denial ACEs for each owner or allowed user */ + + case POSIX_ACL_USER : + case POSIX_ACL_USER_OBJ : + + ok = build_user_denials(pacl, + usid, mapping, flags, pxace, pset); + break; + default : + break; + } + } + + /* + * for directories, insert a world execution denial + * inherited to plain files. + * This is to prevent Windows from granting execution + * of files through inheritance from parent directory + */ + + if (isdir && ok) { + pos = le16_to_cpu(pacl->size); + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE; + pdace->size = cpu_to_le16(wsidsz + 8); + pdace->mask = FILE_EXEC; + memcpy((char*)&pdace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + } + + /* + * now insert (if needed) + * - grants to owner and designated users + * - mask and denials for all groups + * - grants to other + */ + + for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) { + if (i >= pxdesc->acccnt) { + flags = INHERIT_ONLY_ACE + | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + pset = &aceset[1]; + pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt]; + } else { + if (pxdesc->defcnt) + flags = NO_PROPAGATE_INHERIT_ACE; + else + flags = (isdir ? DIR_INHERITANCE + : FILE_INHERITANCE); + pset = &aceset[0]; + pxace = &pxdesc->acl.ace[i]; + } + tag = pxace->tag; + perms = pxace->perms; + switch (tag) { + + /* ACE for each owner or allowed user */ + + case POSIX_ACL_USER : + case POSIX_ACL_USER_OBJ : + ok = build_user_grants(pacl,usid, + mapping,flags,pxace,pset); + break; + + case POSIX_ACL_GROUP : + case POSIX_ACL_GROUP_OBJ : + + /* denials and grants for groups */ + + ok = build_group_denials_grant(pacl,gsid, + mapping,flags,pxace,pset); + break; + + case POSIX_ACL_OTHER : + + /* grants for other users */ + + pos = le16_to_cpu(pacl->size); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + grants = WORLD_RIGHTS; + if (isdir) { + if (perms & POSIX_PERM_X) + grants |= DIR_EXEC; + if (perms & POSIX_PERM_W) + grants |= DIR_WRITE; + if (perms & POSIX_PERM_R) + grants |= DIR_READ; + } else { + if (perms & POSIX_PERM_X) + grants |= FILE_EXEC; + if (perms & POSIX_PERM_W) + grants |= FILE_WRITE; + if (perms & POSIX_PERM_R) + grants |= FILE_READ; + } + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(wsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt = le16_to_cpu(pacl->ace_count) + 1; + pacl->ace_count = cpu_to_le16(acecnt); + pacl->size = cpu_to_le16(pos); + break; + } + } + + if (!ok) { + errno = EINVAL; + pos = 0; + } else { + /* an ACE for administrators */ + /* always full access */ + + pos = le16_to_cpu(pacl->size); + acecnt = le16_to_cpu(pacl->ace_count); + if (isdir) + flags = OBJECT_INHERIT_ACE + | CONTAINER_INHERIT_ACE; + else + flags = NO_PROPAGATE_INHERIT_ACE; + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(asidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, adminsid, asidsz); + pos += asidsz + 8; + acecnt++; + + /* an ACE for system (needed ?) */ + /* always full access */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = flags; + pgace->size = cpu_to_le16(ssidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, systemsid, ssidsz); + pos += ssidsz + 8; + acecnt++; + + /* a null ACE to hold special flags */ + /* using the same representation as cygwin */ + + if (mode & (S_ISVTX | S_ISGID | S_ISUID)) { + nsidsz = ntfs_sid_size(nullsid); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = NO_PROPAGATE_INHERIT_ACE; + pgace->size = cpu_to_le16(nsidsz + 8); + grants = const_cpu_to_le32(0); + if (mode & S_ISUID) + grants |= FILE_APPEND_DATA; + if (mode & S_ISGID) + grants |= FILE_WRITE_DATA; + if (mode & S_ISVTX) + grants |= FILE_READ_DATA; + pgace->mask = grants; + memcpy((char*)&pgace->sid, nullsid, nsidsz); + pos += nsidsz + 8; + acecnt++; + } + + /* fix ACL header */ + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + } + return (ok ? pos : 0); +} + +#endif /* POSIXACLS */ + +static int buildacls(char *secattr, int offs, mode_t mode, int isdir, + const SID * usid, const SID * gsid) +{ + ACL *pacl; + ACCESS_ALLOWED_ACE *pgace; + ACCESS_ALLOWED_ACE *pdace; + BOOL adminowns; + BOOL groupowns; + ACE_FLAGS gflags; + int pos; + int acecnt; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + int nsidsz; + le32 grants; + le32 denials; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + adminowns = ntfs_same_sid(usid, adminsid) + || ntfs_same_sid(gsid, adminsid); + groupowns = !adminowns && ntfs_same_sid(usid, gsid); + + /* ACL header */ + pacl = (ACL*)&secattr[offs]; + pacl->revision = ACL_REVISION; + pacl->alignment1 = 0; + pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8); + pacl->ace_count = const_cpu_to_le16(1); + pacl->alignment2 = const_cpu_to_le16(0); + pos = sizeof(ACL); + acecnt = 0; + + /* compute a grant ACE for owner */ + /* this ACE will be inserted after denial for owner */ + + grants = OWNER_RIGHTS; + if (isdir) { + gflags = DIR_INHERITANCE; + if (mode & S_IXUSR) + grants |= DIR_EXEC; + if (mode & S_IWUSR) + grants |= DIR_WRITE; + if (mode & S_IRUSR) + grants |= DIR_READ; + } else { + gflags = FILE_INHERITANCE; + if (mode & S_IXUSR) + grants |= FILE_EXEC; + if (mode & S_IWUSR) + grants |= FILE_WRITE; + if (mode & S_IRUSR) + grants |= FILE_READ; + } + + /* a possible ACE to deny owner what he/she would */ + /* induely get from administrator, group or world */ + /* unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + if (!adminowns) { + if (!groupowns) { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if (mode & (S_IXGRP | S_IXOTH)) + denials |= DIR_EXEC; + if (mode & (S_IWGRP | S_IWOTH)) + denials |= DIR_WRITE; + if (mode & (S_IRGRP | S_IROTH)) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if (mode & (S_IXGRP | S_IXOTH)) + denials |= FILE_EXEC; + if (mode & (S_IWGRP | S_IWOTH)) + denials |= FILE_WRITE; + if (mode & (S_IRGRP | S_IROTH)) + denials |= FILE_READ; + } + } else { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if ((mode & S_IXOTH) && !(mode & S_IXGRP)) + denials |= DIR_EXEC; + if ((mode & S_IWOTH) && !(mode & S_IWGRP)) + denials |= DIR_WRITE; + if ((mode & S_IROTH) && !(mode & S_IRGRP)) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if ((mode & S_IXOTH) && !(mode & S_IXGRP)) + denials |= FILE_EXEC; + if ((mode & S_IWOTH) && !(mode & S_IWGRP)) + denials |= FILE_WRITE; + if ((mode & S_IROTH) && !(mode & S_IRGRP)) + denials |= FILE_READ; + } + } + denials &= ~grants; + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->size = cpu_to_le16(usidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, usid, usidsz); + pos += usidsz + 8; + acecnt++; + } + } + /* + * for directories, a world execution denial + * inherited to plain files + */ + + if (isdir) { + pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos]; + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE; + pdace->size = cpu_to_le16(wsidsz + 8); + pdace->mask = FILE_EXEC; + memcpy((char*)&pdace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt++; + } + + + /* now insert grants to owner */ + pgace = (ACCESS_ALLOWED_ACE*) &secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->size = cpu_to_le16(usidsz + 8); + pgace->flags = gflags; + pgace->mask = grants; + memcpy((char*)&pgace->sid, usid, usidsz); + pos += usidsz + 8; + acecnt++; + + /* a grant ACE for group */ + /* unless group has the same rights as world */ + /* but present if group is owner or owner is administrator */ + /* this ACE will be inserted after denials for group */ + + if (adminowns + || groupowns + || (((mode >> 3) ^ mode) & 7)) { + grants = WORLD_RIGHTS; + if (isdir) { + gflags = DIR_INHERITANCE; + if (mode & S_IXGRP) + grants |= DIR_EXEC; + if (mode & S_IWGRP) + grants |= DIR_WRITE; + if (mode & S_IRGRP) + grants |= DIR_READ; + } else { + gflags = FILE_INHERITANCE; + if (mode & S_IXGRP) + grants |= FILE_EXEC; + if (mode & S_IWGRP) + grants |= FILE_WRITE; + if (mode & S_IRGRP) + grants |= FILE_READ; + } + + /* a possible ACE to deny group what it would get from world */ + /* or administrator, unless owner is administrator or group */ + + denials = const_cpu_to_le32(0); + pdace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + if (!adminowns && !groupowns) { + if (isdir) { + pdace->flags = DIR_INHERITANCE; + if (mode & S_IXOTH) + denials |= DIR_EXEC; + if (mode & S_IWOTH) + denials |= DIR_WRITE; + if (mode & S_IROTH) + denials |= DIR_READ; + } else { + pdace->flags = FILE_INHERITANCE; + if (mode & S_IXOTH) + denials |= FILE_EXEC; + if (mode & S_IWOTH) + denials |= FILE_WRITE; + if (mode & S_IROTH) + denials |= FILE_READ; + } + denials &= ~(grants | OWNER_RIGHTS); + if (denials) { + pdace->type = ACCESS_DENIED_ACE_TYPE; + pdace->size = cpu_to_le16(gsidsz + 8); + pdace->mask = denials; + memcpy((char*)&pdace->sid, gsid, gsidsz); + pos += gsidsz + 8; + acecnt++; + } + } + + if (adminowns + || groupowns + || ((mode >> 3) & ~mode & 7)) { + /* now insert grants to group */ + /* if more rights than other */ + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = gflags; + pgace->size = cpu_to_le16(gsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, gsid, gsidsz); + pos += gsidsz + 8; + acecnt++; + } + } + + /* an ACE for world users */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + grants = WORLD_RIGHTS; + if (isdir) { + pgace->flags = DIR_INHERITANCE; + if (mode & S_IXOTH) + grants |= DIR_EXEC; + if (mode & S_IWOTH) + grants |= DIR_WRITE; + if (mode & S_IROTH) + grants |= DIR_READ; + } else { + pgace->flags = FILE_INHERITANCE; + if (mode & S_IXOTH) + grants |= FILE_EXEC; + if (mode & S_IWOTH) + grants |= FILE_WRITE; + if (mode & S_IROTH) + grants |= FILE_READ; + } + pgace->size = cpu_to_le16(wsidsz + 8); + pgace->mask = grants; + memcpy((char*)&pgace->sid, worldsid, wsidsz); + pos += wsidsz + 8; + acecnt++; + + /* an ACE for administrators */ + /* always full access */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + if (isdir) + pgace->flags = DIR_INHERITANCE; + else + pgace->flags = FILE_INHERITANCE; + pgace->size = cpu_to_le16(asidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, adminsid, asidsz); + pos += asidsz + 8; + acecnt++; + + /* an ACE for system (needed ?) */ + /* always full access */ + + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + if (isdir) + pgace->flags = DIR_INHERITANCE; + else + pgace->flags = FILE_INHERITANCE; + pgace->size = cpu_to_le16(ssidsz + 8); + grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC; + pgace->mask = grants; + memcpy((char*)&pgace->sid, systemsid, ssidsz); + pos += ssidsz + 8; + acecnt++; + + /* a null ACE to hold special flags */ + /* using the same representation as cygwin */ + + if (mode & (S_ISVTX | S_ISGID | S_ISUID)) { + nsidsz = ntfs_sid_size(nullsid); + pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; + pgace->type = ACCESS_ALLOWED_ACE_TYPE; + pgace->flags = NO_PROPAGATE_INHERIT_ACE; + pgace->size = cpu_to_le16(nsidsz + 8); + grants = const_cpu_to_le32(0); + if (mode & S_ISUID) + grants |= FILE_APPEND_DATA; + if (mode & S_ISGID) + grants |= FILE_WRITE_DATA; + if (mode & S_ISVTX) + grants |= FILE_READ_DATA; + pgace->mask = grants; + memcpy((char*)&pgace->sid, nullsid, nsidsz); + pos += nsidsz + 8; + acecnt++; + } + + /* fix ACL header */ + pacl->size = cpu_to_le16(pos); + pacl->ace_count = cpu_to_le16(acecnt); + return (pos); +} + +#if POSIXACLS + +/* + * Build a full security descriptor from a Posix ACL + * returns descriptor in allocated memory, must free() after use + */ + +char *ntfs_build_descr_posix(struct MAPPING* const mapping[], + struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid) +{ + int newattrsz; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + char *newattr; + int aclsz; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + int k; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + + /* allocate enough space for the new security attribute */ + newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */ + + usidsz + gsidsz /* usid and gsid */ + + sizeof(ACL) /* acl header */ + + 2*(8 + usidsz) /* two possible ACE for user */ + + 3*(8 + gsidsz) /* three possible ACE for group and mask */ + + 8 + wsidsz /* one ACE for world */ + + 8 + asidsz /* one ACE for admin */ + + 8 + ssidsz; /* one ACE for system */ + if (isdir) /* a world denial for directories */ + newattrsz += 8 + wsidsz; + if (pxdesc->mode & 07000) /* a NULL ACE for special modes */ + newattrsz += 8 + ntfs_sid_size(nullsid); + /* account for non-owning users and groups */ + for (k=0; k<pxdesc->acccnt; k++) { + if ((pxdesc->acl.ace[k].tag == POSIX_ACL_USER) + || (pxdesc->acl.ace[k].tag == POSIX_ACL_GROUP)) + newattrsz += 3*40; /* fixme : maximum size */ + } + /* account for default ACE's */ + newattrsz += 2*40*pxdesc->defcnt; /* fixme : maximum size */ + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + /* build the main header part */ + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + /* + * The flag SE_DACL_PROTECTED prevents the ACL + * to be changed in an inheritance after creation + */ + pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED + | SE_SELF_RELATIVE; + /* + * Windows prefers ACL first, do the same to + * get the same hash value and avoid duplication + */ + /* build permissions */ + aclsz = buildacls_posix(mapping,newattr, + sizeof(SECURITY_DESCRIPTOR_RELATIVE), + pxdesc, isdir, usid, gsid); + if (aclsz && ((int)(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz + gsidsz) <= newattrsz)) { + /* append usid and gsid */ + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz], usid, usidsz); + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz], gsid, gsidsz); + /* positions of ACL, USID and GSID into header */ + pnhead->owner = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz); + pnhead->group = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz); + pnhead->sacl = const_cpu_to_le32(0); + pnhead->dacl = + const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + } else { + /* ACL failure (errno set) or overflow */ + free(newattr); + newattr = (char*)NULL; + if (aclsz) { + /* hope error was detected before overflowing */ + ntfs_log_error("Security descriptor is longer than expected\n"); + errno = EIO; + } + } + } else + errno = ENOMEM; + return (newattr); +} + +#endif /* POSIXACLS */ + +/* + * Build a full security descriptor + * returns descriptor in allocated memory, must free() after use + */ + +char *ntfs_build_descr(mode_t mode, + int isdir, const SID * usid, const SID * gsid) +{ + int newattrsz; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + char *newattr; + int aclsz; + int usidsz; + int gsidsz; + int wsidsz; + int asidsz; + int ssidsz; + + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + wsidsz = ntfs_sid_size(worldsid); + asidsz = ntfs_sid_size(adminsid); + ssidsz = ntfs_sid_size(systemsid); + + /* allocate enough space for the new security attribute */ + newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */ + + usidsz + gsidsz /* usid and gsid */ + + sizeof(ACL) /* acl header */ + + 2*(8 + usidsz) /* two possible ACE for user */ + + 2*(8 + gsidsz) /* two possible ACE for group */ + + 8 + wsidsz /* one ACE for world */ + + 8 + asidsz /* one ACE for admin */ + + 8 + ssidsz; /* one ACE for system */ + if (isdir) /* a world denial for directories */ + newattrsz += 8 + wsidsz; + if (mode & 07000) /* a NULL ACE for special modes */ + newattrsz += 8 + ntfs_sid_size(nullsid); + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + /* build the main header part */ + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*) newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + /* + * The flag SE_DACL_PROTECTED prevents the ACL + * to be changed in an inheritance after creation + */ + pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED + | SE_SELF_RELATIVE; + /* + * Windows prefers ACL first, do the same to + * get the same hash value and avoid duplication + */ + /* build permissions */ + aclsz = buildacls(newattr, + sizeof(SECURITY_DESCRIPTOR_RELATIVE), + mode, isdir, usid, gsid); + if (((int)sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz + gsidsz) <= newattrsz) { + /* append usid and gsid */ + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz], usid, usidsz); + memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz], gsid, gsidsz); + /* positions of ACL, USID and GSID into header */ + pnhead->owner = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz); + pnhead->group = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE) + + aclsz + usidsz); + pnhead->sacl = const_cpu_to_le32(0); + pnhead->dacl = + const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + } else { + /* hope error was detected before overflowing */ + free(newattr); + newattr = (char*)NULL; + ntfs_log_error("Security descriptor is longer than expected\n"); + errno = EIO; + } + } else + errno = ENOMEM; + return (newattr); +} + +/* + * Create a mode_t permission set + * from owner, group and world grants as represented in ACEs + */ + +static int merge_permissions(BOOL isdir, + le32 owner, le32 group, le32 world, le32 special) + +{ + int perm; + + perm = 0; + /* build owner permission */ + if (owner) { + if (isdir) { + /* exec if any of list, traverse */ + if (owner & DIR_GEXEC) + perm |= S_IXUSR; + /* write if any of addfile, adddir, delchild */ + if (owner & DIR_GWRITE) + perm |= S_IWUSR; + /* read if any of list */ + if (owner & DIR_GREAD) + perm |= S_IRUSR; + } else { + /* exec if execute or generic execute */ + if (owner & FILE_GEXEC) + perm |= S_IXUSR; + /* write if any of writedata or generic write */ + if (owner & FILE_GWRITE) + perm |= S_IWUSR; + /* read if any of readdata or generic read */ + if (owner & FILE_GREAD) + perm |= S_IRUSR; + } + } + /* build group permission */ + if (group) { + if (isdir) { + /* exec if any of list, traverse */ + if (group & DIR_GEXEC) + perm |= S_IXGRP; + /* write if any of addfile, adddir, delchild */ + if (group & DIR_GWRITE) + perm |= S_IWGRP; + /* read if any of list */ + if (group & DIR_GREAD) + perm |= S_IRGRP; + } else { + /* exec if execute */ + if (group & FILE_GEXEC) + perm |= S_IXGRP; + /* write if any of writedata, appenddata */ + if (group & FILE_GWRITE) + perm |= S_IWGRP; + /* read if any of readdata */ + if (group & FILE_GREAD) + perm |= S_IRGRP; + } + } + /* build world permission */ + if (world) { + if (isdir) { + /* exec if any of list, traverse */ + if (world & DIR_GEXEC) + perm |= S_IXOTH; + /* write if any of addfile, adddir, delchild */ + if (world & DIR_GWRITE) + perm |= S_IWOTH; + /* read if any of list */ + if (world & DIR_GREAD) + perm |= S_IROTH; + } else { + /* exec if execute */ + if (world & FILE_GEXEC) + perm |= S_IXOTH; + /* write if any of writedata, appenddata */ + if (world & FILE_GWRITE) + perm |= S_IWOTH; + /* read if any of readdata */ + if (world & FILE_GREAD) + perm |= S_IROTH; + } + } + /* build special permission flags */ + if (special) { + if (special & FILE_APPEND_DATA) + perm |= S_ISUID; + if (special & FILE_WRITE_DATA) + perm |= S_ISGID; + if (special & FILE_READ_DATA) + perm |= S_ISVTX; + } + return (perm); +} + +#if POSIXACLS + +/* + * Normalize a Posix ACL either from a sorted raw set of + * access ACEs or default ACEs + * (standard case : different owner, group and administrator) + */ + +static int norm_std_permissions_posix(struct POSIX_SECURITY *posix_desc, + BOOL groupowns, int start, int count, int target) +{ + int j,k; + s32 id; + u16 tag; + u16 tagsset; + struct POSIX_ACE *pxace; + mode_t grantgrps; + mode_t grantwrld; + mode_t denywrld; + mode_t allow; + mode_t deny; + mode_t perms; + mode_t mode; + + mode = 0; + tagsset = 0; + /* + * Determine what is granted to some group or world + * Also get denials to world which are meant to prevent + * execution flags to be inherited by plain files + */ + pxace = posix_desc->acl.ace; + grantgrps = 0; + grantwrld = 0; + denywrld = 0; + for (j=start; j<(start + count); j++) { + if (pxace[j].perms & POSIX_PERM_DENIAL) { + /* deny world exec unless for default */ + if ((pxace[j].tag == POSIX_ACL_OTHER) + && !start) + denywrld = pxace[j].perms; + } else { + switch (pxace[j].tag) { + case POSIX_ACL_GROUP_OBJ : + grantgrps |= pxace[j].perms; + break; + case POSIX_ACL_GROUP : + if (pxace[j].id) + grantgrps |= pxace[j].perms; + break; + case POSIX_ACL_OTHER : + grantwrld = pxace[j].perms; + break; + default : + break; + } + } + } + /* + * Collect groups of ACEs related to the same id + * and determine what is granted and what is denied. + * It is important the ACEs have been sorted + */ + j = start; + k = target; + while (j < (start + count)) { + tag = pxace[j].tag; + id = pxace[j].id; + if (pxace[j].perms & POSIX_PERM_DENIAL) { + deny = pxace[j].perms | denywrld; + allow = 0; + } else { + deny = denywrld; + allow = pxace[j].perms; + } + j++; + while ((j < (start + count)) + && (pxace[j].tag == tag) + && (pxace[j].id == id)) { + if (pxace[j].perms & POSIX_PERM_DENIAL) + deny |= pxace[j].perms; + else + allow |= pxace[j].perms; + j++; + } + /* + * Build the permissions equivalent to grants and denials + */ + if (groupowns) { + if (tag == POSIX_ACL_MASK) + perms = ~deny; + else + perms = allow & ~deny; + } else + switch (tag) { + case POSIX_ACL_USER_OBJ : + perms = (allow | grantgrps | grantwrld) & ~deny; + break; + case POSIX_ACL_USER : + if (id) + perms = (allow | grantgrps | grantwrld) + & ~deny; + else + perms = allow; + break; + case POSIX_ACL_GROUP_OBJ : + perms = (allow | grantwrld) & ~deny; + break; + case POSIX_ACL_GROUP : + if (id) + perms = (allow | grantwrld) & ~deny; + else + perms = allow; + break; + case POSIX_ACL_MASK : + perms = ~deny; + break; + default : + perms = allow & ~deny; + break; + } + /* + * Store into a Posix ACE + */ + if (tag != POSIX_ACL_SPECIAL) { + pxace[k].tag = tag; + pxace[k].id = id; + pxace[k].perms = perms + & (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + tagsset |= tag; + k++; + } + switch (tag) { + case POSIX_ACL_USER_OBJ : + mode |= ((perms & 7) << 6); + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= perms & 7; + break; + case POSIX_ACL_SPECIAL : + mode |= (perms & (S_ISVTX | S_ISUID | S_ISGID)); + break; + default : + break; + } + } + if (!start) { /* not satisfactory */ + posix_desc->mode = mode; + posix_desc->tagsset = tagsset; + } + return (k - target); +} + +#endif /* POSIXACLS */ + +/* + * Interpret an ACL and extract meaningful grants + * (standard case : different owner, group and administrator) + */ + +static int build_std_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL noown; + le32 special; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + noown = TRUE; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE)) { + if (ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) { + noown = FALSE; + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowown |= pace->mask; + else if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyown |= pace->mask; + } else + if (ntfs_same_sid(gsid, &pace->sid) + && !(pace->mask & WRITE_OWNER)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowgrp |= pace->mask; + else if (pace->type == ACCESS_DENIED_ACE_TYPE) + denygrp |= pace->mask; + } else + if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } else + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + } + offace += le16_to_cpu(pace->size); + } + /* + * No indication about owner's rights : grant basic rights + * This happens for files created by Windows in directories + * created by Linux and owned by root, because Windows + * merges the admin ACEs + */ + if (noown) + allowown = (FILE_READ_DATA | FILE_WRITE_DATA | FILE_EXECUTE); + /* + * Add to owner rights granted to group or world + * unless denied personaly, and add to group rights + * granted to world unless denied specifically + */ + allowown |= (allowgrp | allowall); + allowgrp |= allowall; + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); +} + +/* + * Interpret an ACL and extract meaningful grants + * (special case : owner and group are the same, + * and not administrator) + */ + +static int build_owngrp_permissions(const char *securattr, + const SID *usid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + le32 special; + BOOL grppresent; + BOOL ownpresent; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + ownpresent = FALSE; + grppresent = FALSE; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE)) { + if ((ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) + && (pace->mask & WRITE_OWNER)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowown |= pace->mask; + ownpresent = TRUE; + } + } else + if (ntfs_same_sid(usid, &pace->sid) + && (!(pace->mask & WRITE_OWNER))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowgrp |= pace->mask; + grppresent = TRUE; + } + } else + if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } else + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + } + offace += le16_to_cpu(pace->size); + } + if (!ownpresent) + allowown = allowall; + if (!grppresent) + allowgrp = allowall; + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); +} + +#if POSIXACLS + +/* + * Normalize a Posix ACL either from a sorted raw set of + * access ACEs or default ACEs + * (special case : owner or/and group is administrator) + */ + +static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc, + int start, int count, int target) +{ + int j,k; + s32 id; + u16 tag; + u16 tagsset; + struct POSIX_ACE *pxace; + mode_t denywrld; + mode_t allow; + mode_t deny; + mode_t perms; + mode_t mode; + + mode = 0; + pxace = posix_desc->acl.ace; + tagsset = 0; + denywrld = 0; + /* + * Get denials to world which are meant to prevent + * execution flags to be inherited by plain files + */ + for (j=start; j<(start + count); j++) { + if (pxace[j].perms & POSIX_PERM_DENIAL) { + /* deny world exec not for default */ + if ((pxace[j].tag == POSIX_ACL_OTHER) + && !start) + denywrld = pxace[j].perms; + } + } + /* + * Collect groups of ACEs related to the same id + * and determine what is granted (denials are ignored) + * It is important the ACEs have been sorted + */ + j = start; + k = target; + deny = 0; + while (j < (start + count)) { + allow = 0; + tag = pxace[j].tag; + id = pxace[j].id; + if (tag == POSIX_ACL_MASK) { + deny = pxace[j].perms; + j++; + while ((j < (start + count)) + && (pxace[j].tag == POSIX_ACL_MASK)) + j++; + } else { + if (!(pxace[j].perms & POSIX_PERM_DENIAL)) + allow = pxace[j].perms; + j++; + while ((j < (start + count)) + && (pxace[j].tag == tag) + && (pxace[j].id == id)) { + if (!(pxace[j].perms & POSIX_PERM_DENIAL)) + allow |= pxace[j].perms; + j++; + } + } + + /* + * Store the grants into a Posix ACE + */ + if (tag == POSIX_ACL_MASK) + perms = ~deny; + else + perms = allow & ~denywrld; + if (tag != POSIX_ACL_SPECIAL) { + pxace[k].tag = tag; + pxace[k].id = id; + pxace[k].perms = perms + & (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X); + tagsset |= tag; + k++; + } + switch (tag) { + case POSIX_ACL_USER_OBJ : + mode |= ((perms & 7) << 6); + break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_MASK : + mode = (mode & 07707) | ((perms & 7) << 3); + break; + case POSIX_ACL_OTHER : + mode |= perms & 7; + break; + case POSIX_ACL_SPECIAL : + mode |= perms & (S_ISVTX | S_ISUID | S_ISGID); + break; + default : + break; + } + } + if (!start) { /* not satisfactory */ + posix_desc->mode = mode; + posix_desc->tagsset = tagsset; + } + return (k - target); +} + +#endif /* POSIXACLS */ + +/* + * Interpret an ACL and extract meaningful grants + * (special case : owner or/and group is administrator) + */ + + +static int build_ownadmin_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL firstapply; + int isforeign; + le32 special; + le32 allowown, allowgrp, allowall; + le32 denyown, denygrp, denyall; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + special = const_cpu_to_le32(0); + allowown = allowgrp = allowall = const_cpu_to_le32(0); + denyown = denygrp = denyall = const_cpu_to_le32(0); + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + firstapply = TRUE; + isforeign = 3; + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE) + && !(~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK))) { + if ((ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) + && (((pace->mask & WRITE_OWNER) && firstapply))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowown |= pace->mask; + isforeign &= ~1; + } else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyown |= pace->mask; + } else + if (ntfs_same_sid(gsid, &pace->sid) + && (!(pace->mask & WRITE_OWNER))) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) { + allowgrp |= pace->mask; + isforeign &= ~2; + } else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denygrp |= pace->mask; + } else if (is_world_sid((const SID*)&pace->sid)) { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + allowall |= pace->mask; + else + if (pace->type == ACCESS_DENIED_ACE_TYPE) + denyall |= pace->mask; + } + firstapply = FALSE; + } else + if (!(pace->flags & INHERIT_ONLY_ACE)) + if ((ntfs_same_sid((const SID*)&pace->sid,nullsid)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) + special |= pace->mask; + offace += le16_to_cpu(pace->size); + } + if (isforeign) { + allowown |= (allowgrp | allowall); + allowgrp |= allowall; + } + return (merge_permissions(isdir, + allowown & ~(denyown | denyall), + allowgrp & ~(denygrp | denyall), + allowall & ~denyall, + special)); +} + +#if OWNERFROMACL + +/* + * Define the owner of a file as the first user allowed + * to change the owner, instead of the user defined as owner. + * + * This produces better approximations for files written by a + * Windows user in an inheritable directory owned by another user, + * as the access rights are inheritable but the ownership is not. + * + * An important case is the directories "Documents and Settings/user" + * which the users must have access to, though Windows considers them + * as owned by administrator. + */ + +const SID *ntfs_acl_owner(const char *securattr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const SID *usid; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + BOOL found; + + found = FALSE; + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + if (offdacl) { + pacl = (const ACL*)&securattr[offdacl]; + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + nace = 0; + do { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if ((pace->mask & WRITE_OWNER) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE) + && ntfs_is_user_sid(&pace->sid)) + found = TRUE; + offace += le16_to_cpu(pace->size); + } while (!found && (++nace < acecnt)); + } + if (found) + usid = &pace->sid; + else + usid = (const SID*)&securattr[le32_to_cpu(phead->owner)]; + return (usid); +} + +#else + +/* + * Special case for files owned by administrator with full + * access granted to a mapped user : consider this user as the tenant + * of the file. + * + * This situation cannot be represented with Linux concepts and can + * only be found for files or directories created by Windows. + * Typical situation : directory "Documents and Settings/user" which + * is on the path to user's files and must be given access to user + * only. + * + * Check file is owned by administrator and no user has rights before + * calling. + * Returns the uid of tenant or zero if none + */ + + +static uid_t find_tenant(struct MAPPING *const mapping[], + const char *securattr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + int offdacl; + int offace; + int acecnt; + int nace; + uid_t tid; + uid_t xid; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + pacl = (const ACL*)&securattr[offdacl]; + tid = 0; + if (offdacl) { + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else + acecnt = 0; + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if ((pace->type == ACCESS_ALLOWED_ACE_TYPE) + && (pace->mask & DIR_WRITE)) { + xid = NTFS_FIND_USER(mapping[MAPUSERS], &pace->sid); + if (xid) tid = xid; + } + offace += le16_to_cpu(pace->size); + } + return (tid); +} + +#endif /* OWNERFROMACL */ + +#if POSIXACLS + +/* + * Build Posix permissions from an ACL + * returns a pointer to the requested permissions + * or a null pointer (with errno set) if there is a problem + * + * If the NTFS ACL was created according to our rules, the retrieved + * Posix ACL should be the exact ACL which was set. However if + * the NTFS ACL was built by a different tool, the result could + * be a a poor approximation of what was expected + */ + +struct POSIX_SECURITY *ntfs_build_permissions_posix( + struct MAPPING *const mapping[], + const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + const ACL *pacl; + const ACCESS_ALLOWED_ACE *pace; + struct POSIX_ACE *pxace; + struct { + uid_t prevuid; + gid_t prevgid; + int groupmasks; + s16 tagsset; + BOOL gotowner; + BOOL gotownermask; + BOOL gotgroup; + mode_t permswrld; + } ctx[2], *pctx; + int offdacl; + int offace; + int alloccnt; + int acecnt; + uid_t uid; + gid_t gid; + int i,j; + int k,l; + BOOL ignore; + BOOL adminowns; + BOOL groupowns; + BOOL firstinh; + BOOL genericinh; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + offdacl = le32_to_cpu(phead->dacl); + if (offdacl) { + pacl = (const ACL*)&securattr[offdacl]; + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + } else { + acecnt = 0; + offace = 0; + } + adminowns = FALSE; + groupowns = ntfs_same_sid(gsid,usid); + firstinh = FALSE; + genericinh = FALSE; + /* + * Build a raw posix security descriptor + * by just translating permissions and ids + * Add 2 to the count of ACE to be able to insert + * a group ACE later in access and default ACLs + * and add 2 more to be able to insert ACEs for owner + * and 2 more for other + */ + alloccnt = acecnt + 6; + pxdesc = (struct POSIX_SECURITY*)malloc( + sizeof(struct POSIX_SECURITY) + + alloccnt*sizeof(struct POSIX_ACE)); + k = 0; + l = alloccnt; + for (i=0; i<2; i++) { + pctx = &ctx[i]; + pctx->permswrld = 0; + pctx->prevuid = -1; + pctx->prevgid = -1; + pctx->groupmasks = 0; + pctx->tagsset = 0; + pctx->gotowner = FALSE; + pctx->gotgroup = FALSE; + pctx->gotownermask = FALSE; + } + for (j=0; j<acecnt; j++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (pace->flags & INHERIT_ONLY_ACE) { + pxace = &pxdesc->acl.ace[l - 1]; + pctx = &ctx[1]; + } else { + pxace = &pxdesc->acl.ace[k]; + pctx = &ctx[0]; + } + ignore = FALSE; + /* + * grants for root as a designated user or group + */ + if ((~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE) + && ntfs_same_sid(&pace->sid, adminsid)) { + pxace->tag = (pace->mask & ROOT_OWNER_UNMARK ? POSIX_ACL_GROUP : POSIX_ACL_USER); + pxace->id = 0; + if ((pace->mask & (GENERIC_ALL | WRITE_OWNER)) + && (pace->flags & INHERIT_ONLY_ACE)) + ignore = genericinh = TRUE; + } else + if (ntfs_same_sid(usid, &pace->sid)) { + pxace->id = -1; + /* + * Owner has no write-owner right : + * a group was defined same as owner + * or admin was owner or group : + * denials are meant to owner + * and grants are meant to group + */ + if (!(pace->mask & (WRITE_OWNER | GENERIC_ALL)) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) { + if (ntfs_same_sid(gsid,usid)) { + pxace->tag = POSIX_ACL_GROUP_OBJ; + pxace->id = -1; + } else { + if (ntfs_same_sid(&pace->sid,usid)) + groupowns = TRUE; + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + pxace->tag = POSIX_ACL_GROUP; + pxace->id = gid; + pctx->prevgid = gid; + } else { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + if (uid) { + pxace->tag = POSIX_ACL_USER; + pxace->id = uid; + } else + ignore = TRUE; + } + } + } else { + /* + * when group owns, late denials for owner + * mean group mask + */ + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER)) { + pxace->tag = POSIX_ACL_MASK; + pctx->gotownermask = TRUE; + if (pctx->gotowner) + pctx->groupmasks++; + } else { + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + pctx->gotowner = TRUE; + if (pctx->gotownermask && !pctx->gotowner) { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + pxace->id = uid; + pxace->tag = POSIX_ACL_USER; + } else + pxace->tag = POSIX_ACL_USER_OBJ; + /* system ignored, and admin */ + /* ignored at first position */ + if (pace->flags & INHERIT_ONLY_ACE) { + if ((firstinh && ntfs_same_sid(&pace->sid,adminsid)) + || ntfs_same_sid(&pace->sid,systemsid)) + ignore = TRUE; + if (!firstinh) { + firstinh = TRUE; + } + } else { + if ((adminowns && ntfs_same_sid(&pace->sid,adminsid)) + || ntfs_same_sid(&pace->sid,systemsid)) + ignore = TRUE; + if (ntfs_same_sid(usid,adminsid)) + adminowns = TRUE; + } + } + } + } else if (ntfs_same_sid(gsid, &pace->sid)) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER)) { + pxace->tag = POSIX_ACL_MASK; + pxace->id = -1; + if (pctx->gotowner) + pctx->groupmasks++; + } else { + if (pctx->gotgroup || (pctx->groupmasks > 1)) { + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + pxace->id = gid; + pxace->tag = POSIX_ACL_GROUP; + pctx->prevgid = gid; + } else + ignore = TRUE; + } else { + pxace->id = -1; + pxace->tag = POSIX_ACL_GROUP_OBJ; + if (pace->type == ACCESS_ALLOWED_ACE_TYPE) + pctx->gotgroup = TRUE; + } + + if (ntfs_same_sid(gsid,adminsid) + || ntfs_same_sid(gsid,systemsid)) { + if (pace->mask & (WRITE_OWNER | GENERIC_ALL)) + ignore = TRUE; + if (ntfs_same_sid(gsid,adminsid)) + adminowns = TRUE; + else + genericinh = ignore; + } + } + } else if (is_world_sid((const SID*)&pace->sid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_OTHER; + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->flags & INHERIT_ONLY_ACE)) + ignore = TRUE; + } else if (ntfs_same_sid((const SID*)&pace->sid,nullsid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_SPECIAL; + } else { + uid = NTFS_FIND_USER(mapping[MAPUSERS],&pace->sid); + if (uid) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER) + && (pctx->prevuid != uid)) { + pxace->id = -1; + pxace->tag = POSIX_ACL_MASK; + } else { + pxace->id = uid; + pxace->tag = POSIX_ACL_USER; + } + pctx->prevuid = uid; + } else { + gid = NTFS_FIND_GROUP(mapping[MAPGROUPS],&pace->sid); + if (gid) { + if ((pace->type == ACCESS_DENIED_ACE_TYPE) + && (pace->mask & WRITE_OWNER) + && (pctx->prevgid != gid)) { + pxace->tag = POSIX_ACL_MASK; + pctx->groupmasks++; + } else { + pxace->tag = POSIX_ACL_GROUP; + } + pxace->id = gid; + pctx->prevgid = gid; + } else { + /* + * do not grant rights to unknown + * people and do not define root as a + * designated user or group + */ + ignore = TRUE; + } + } + } + if (!ignore) { + pxace->perms = 0; + /* specific decoding for vtx/uid/gid */ + if (pxace->tag == POSIX_ACL_SPECIAL) { + if (pace->mask & FILE_APPEND_DATA) + pxace->perms |= S_ISUID; + if (pace->mask & FILE_WRITE_DATA) + pxace->perms |= S_ISGID; + if (pace->mask & FILE_READ_DATA) + pxace->perms |= S_ISVTX; + } else + if (isdir) { + if (pace->mask & DIR_GEXEC) + pxace->perms |= POSIX_PERM_X; + if (pace->mask & DIR_GWRITE) + pxace->perms |= POSIX_PERM_W; + if (pace->mask & DIR_GREAD) + pxace->perms |= POSIX_PERM_R; + if ((pace->mask & GENERIC_ALL) + && (pace->flags & INHERIT_ONLY_ACE)) + pxace->perms |= POSIX_PERM_X + | POSIX_PERM_W + | POSIX_PERM_R; + } else { + if (pace->mask & FILE_GEXEC) + pxace->perms |= POSIX_PERM_X; + if (pace->mask & FILE_GWRITE) + pxace->perms |= POSIX_PERM_W; + if (pace->mask & FILE_GREAD) + pxace->perms |= POSIX_PERM_R; + } + + if (pace->type != ACCESS_ALLOWED_ACE_TYPE) + pxace->perms |= POSIX_PERM_DENIAL; + else + if (pxace->tag == POSIX_ACL_OTHER) + pctx->permswrld |= pxace->perms; + pctx->tagsset |= pxace->tag; + if (pace->flags & INHERIT_ONLY_ACE) { + l--; + } else { + k++; + } + } + offace += le16_to_cpu(pace->size); + } + /* + * Create world perms if none (both lists) + */ + for (i=0; i<2; i++) + if ((genericinh || !i) + && !(ctx[i].tagsset & POSIX_ACL_OTHER)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_OTHER; + pxace->id = -1; + pxace->perms = 0; + ctx[i].tagsset |= POSIX_ACL_OTHER; + ctx[i].permswrld = 0; + } + /* + * Set basic owner perms if none (both lists) + * This happens for files created by Windows in directories + * created by Linux and owned by root, because Windows + * merges the admin ACEs + */ + for (i=0; i<2; i++) + if (!(ctx[i].tagsset & POSIX_ACL_USER_OBJ) + && (ctx[i].tagsset & POSIX_ACL_OTHER)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_USER_OBJ; + pxace->id = -1; + pxace->perms = POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X; + ctx[i].tagsset |= POSIX_ACL_USER_OBJ; + } + /* + * Duplicate world perms as group_obj perms if none + */ + for (i=0; i<2; i++) + if ((ctx[i].tagsset & POSIX_ACL_OTHER) + && !(ctx[i].tagsset & POSIX_ACL_GROUP_OBJ)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_GROUP_OBJ; + pxace->id = -1; + pxace->perms = ctx[i].permswrld; + ctx[i].tagsset |= POSIX_ACL_GROUP_OBJ; + } + /* + * Also duplicate world perms as group perms if they + * were converted to mask and not followed by a group entry + */ + if (ctx[0].groupmasks) { + for (j=k-2; j>=0; j--) { + if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + && (pxdesc->acl.ace[j].id != -1) + && ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP) + || (pxdesc->acl.ace[j+1].id + != pxdesc->acl.ace[j].id))) { + pxace = &pxdesc->acl.ace[k]; + pxace->tag = POSIX_ACL_GROUP; + pxace->id = pxdesc->acl.ace[j].id; + pxace->perms = ctx[0].permswrld; + ctx[0].tagsset |= POSIX_ACL_GROUP; + k++; + } + if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + pxdesc->acl.ace[j].id = -1; + } + } + if (ctx[1].groupmasks) { + for (j=l; j<(alloccnt-1); j++) { + if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + && (pxdesc->acl.ace[j].id != -1) + && ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP) + || (pxdesc->acl.ace[j+1].id + != pxdesc->acl.ace[j].id))) { + pxace = &pxdesc->acl.ace[l - 1]; + pxace->tag = POSIX_ACL_GROUP; + pxace->id = pxdesc->acl.ace[j].id; + pxace->perms = ctx[1].permswrld; + ctx[1].tagsset |= POSIX_ACL_GROUP; + l--; + } + if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK) + pxdesc->acl.ace[j].id = -1; + } + } + + /* + * Insert default mask if none present and + * there are designated users or groups + * (the space for it has not beed used) + */ + for (i=0; i<2; i++) + if ((ctx[i].tagsset & (POSIX_ACL_USER | POSIX_ACL_GROUP)) + && !(ctx[i].tagsset & POSIX_ACL_MASK)) { + if (i) + pxace = &pxdesc->acl.ace[--l]; + else + pxace = &pxdesc->acl.ace[k++]; + pxace->tag = POSIX_ACL_MASK; + pxace->id = -1; + pxace->perms = POSIX_PERM_DENIAL; + ctx[i].tagsset |= POSIX_ACL_MASK; + } + + if (k > l) { + ntfs_log_error("Posix descriptor is longer than expected\n"); + errno = EIO; + free(pxdesc); + pxdesc = (struct POSIX_SECURITY*)NULL; + } else { + pxdesc->acccnt = k; + pxdesc->defcnt = alloccnt - l; + pxdesc->firstdef = l; + pxdesc->tagsset = ctx[0].tagsset; + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + ntfs_sort_posix(pxdesc); + if (adminowns) { + k = norm_ownadmin_permissions_posix(pxdesc, + 0, pxdesc->acccnt, 0); + pxdesc->acccnt = k; + l = norm_ownadmin_permissions_posix(pxdesc, + pxdesc->firstdef, pxdesc->defcnt, k); + pxdesc->firstdef = k; + pxdesc->defcnt = l; + } else { + k = norm_std_permissions_posix(pxdesc,groupowns, + 0, pxdesc->acccnt, 0); + pxdesc->acccnt = k; + l = norm_std_permissions_posix(pxdesc,groupowns, + pxdesc->firstdef, pxdesc->defcnt, k); + pxdesc->firstdef = k; + pxdesc->defcnt = l; + } + } + if (pxdesc && !ntfs_valid_posix(pxdesc)) { + ntfs_log_error("Invalid Posix descriptor built\n"); + errno = EIO; + free(pxdesc); + pxdesc = (struct POSIX_SECURITY*)NULL; + } + return (pxdesc); +} + +#endif /* POSIXACLS */ + +/* + * Build unix-style (mode_t) permissions from an ACL + * returns the requested permissions + * or a negative result (with errno set) if there is a problem + */ + +int ntfs_build_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir) +{ + int perm; + BOOL adminowns; + BOOL groupowns; + + adminowns = ntfs_same_sid(usid,adminsid) + || ntfs_same_sid(gsid,adminsid); + groupowns = !adminowns && ntfs_same_sid(gsid,usid); + if (adminowns) + perm = build_ownadmin_permissions(securattr, usid, gsid, isdir); + else + if (groupowns) + perm = build_owngrp_permissions(securattr, usid, isdir); + else + perm = build_std_permissions(securattr, usid, gsid, isdir); + return (perm); +} + +/* + * The following must be in some library... + */ + +static unsigned long atoul(const char *p) +{ /* must be somewhere ! */ + unsigned long v; + + v = 0; + while ((*p >= '0') && (*p <= '9')) + v = v * 10 + (*p++) - '0'; + return (v); +} + +/* + * Build an internal representation of a SID + * Returns a copy in allocated memory if it succeeds + * The SID is checked to be a valid user one. + */ + +static SID *encodesid(const char *sidstr) +{ + SID *sid; + int cnt; + BIGSID bigsid; + SID *bsid; + u32 auth; + const char *p; + + sid = (SID*) NULL; + if (!strncmp(sidstr, "S-1-", 4)) { + bsid = (SID*)&bigsid; + bsid->revision = SID_REVISION; + p = &sidstr[4]; + auth = atoul(p); + bsid->identifier_authority.high_part = const_cpu_to_be16(0); + bsid->identifier_authority.low_part = cpu_to_be32(auth); + cnt = 0; + p = strchr(p, '-'); + while (p && (cnt < 8)) { + p++; + auth = atoul(p); + bsid->sub_authority[cnt] = cpu_to_le32(auth); + p = strchr(p, '-'); + cnt++; + } + bsid->sub_authority_count = cnt; + if ((cnt > 0) && ntfs_valid_sid(bsid) + && (ntfs_is_user_sid(bsid) || ntfs_known_group_sid(bsid))) { + sid = (SID*) ntfs_malloc(4 * cnt + 8); + if (sid) + memcpy(sid, bsid, 4 * cnt + 8); + } + } + return (sid); +} + +/* + * Get a single mapping item from buffer + * + * Always reads a full line, truncating long lines + * Refills buffer when exhausted + * Returns pointer to item, or NULL when there is no more + */ + +static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid, + off_t *poffs, char *buf, int *psrc, s64 *psize) +{ + int src; + int dst; + char *q; + char *pu; + char *pg; + int gotend; + struct MAPLIST *item; + + src = *psrc; + dst = 0; + /* allocate and get a full line */ + item = (struct MAPLIST*)ntfs_malloc(sizeof(struct MAPLIST)); + if (item) { + do { + gotend = 0; + while ((src < *psize) + && (buf[src] != '\n')) { + if (dst < LINESZ) + item->maptext[dst++] = buf[src]; + src++; + } + if (src >= *psize) { + *poffs += *psize; + *psize = reader(fileid, buf, (size_t)BUFSZ, *poffs); + src = 0; + } else { + gotend = 1; + src++; + item->maptext[dst] = '\0'; + dst = 0; + } + } while (*psize && ((item->maptext[0] == '#') || !gotend)); + if (gotend) { + pu = pg = (char*)NULL; + /* decompose into uid, gid and sid */ + item->uidstr = item->maptext; + item->gidstr = strchr(item->uidstr, ':'); + if (item->gidstr) { + pu = item->gidstr++; + item->sidstr = strchr(item->gidstr, ':'); + if (item->sidstr) { + pg = item->sidstr++; + q = strchr(item->sidstr, ':'); + if (q) *q = 0; + } + } + if (pu && pg) + *pu = *pg = '\0'; + else { + ntfs_log_early_error("Bad mapping item \"%s\"\n", + item->maptext); + free(item); + item = (struct MAPLIST*)NULL; + } + } else { + free(item); /* free unused item */ + item = (struct MAPLIST*)NULL; + } + } + *psrc = src; + return (item); +} + +/* + * Read user mapping file and split into their attribute. + * Parameters are kept as text in a chained list until logins + * are converted to uid. + * Returns the head of list, if any + * + * If an absolute path is provided, the mapping file is assumed + * to be located in another mounted file system, and plain read() + * are used to get its contents. + * If a relative path is provided, the mapping file is assumed + * to be located on the current file system, and internal IO + * have to be used since we are still mounting and we have not + * entered the fuse loop yet. + */ + +struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid) +{ + char buf[BUFSZ]; + struct MAPLIST *item; + struct MAPLIST *firstitem; + struct MAPLIST *lastitem; + int src; + off_t offs; + s64 size; + + firstitem = (struct MAPLIST*)NULL; + lastitem = (struct MAPLIST*)NULL; + offs = 0; + size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0); + if (size > 0) { + src = 0; + do { + item = getmappingitem(reader, fileid, &offs, + buf, &src, &size); + if (item) { + item->next = (struct MAPLIST*)NULL; + if (lastitem) + lastitem->next = item; + else + firstitem = item; + lastitem = item; + } + } while (item); + } + return (firstitem); +} + +/* + * Free memory used to store the user mapping + * The only purpose is to facilitate the detection of memory leaks + */ + +void ntfs_free_mapping(struct MAPPING *mapping[]) +{ + struct MAPPING *user; + struct MAPPING *group; + + /* free user mappings */ + while (mapping[MAPUSERS]) { + user = mapping[MAPUSERS]; + /* do not free SIDs used for group mappings */ + group = mapping[MAPGROUPS]; + while (group && (group->sid != user->sid)) + group = group->next; + if (!group) + free(user->sid); + /* free group list if any */ + if (user->grcnt) + free(user->groups); + /* unchain item and free */ + mapping[MAPUSERS] = user->next; + free(user); + } + /* free group mappings */ + while (mapping[MAPGROUPS]) { + group = mapping[MAPGROUPS]; + free(group->sid); + /* unchain item and free */ + mapping[MAPGROUPS] = group->next; + free(group); + } +} + + +/* + * Build the user mapping list + * user identification may be given in symbolic or numeric format + * + * ! Note ! : does getpwnam() read /etc/passwd or some other file ? + * if so there is a possible recursion into fuse if this + * file is on NTFS, and fuse is not recursion safe. + */ + +struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem) +{ + struct MAPLIST *item; + struct MAPPING *firstmapping; + struct MAPPING *lastmapping; + struct MAPPING *mapping; + struct passwd *pwd; + SID *sid; + int uid; + + firstmapping = (struct MAPPING*)NULL; + lastmapping = (struct MAPPING*)NULL; + for (item = firstitem; item; item = item->next) { + if ((item->uidstr[0] >= '0') && (item->uidstr[0] <= '9')) + uid = atoi(item->uidstr); + else { + uid = 0; + if (item->uidstr[0]) { + pwd = getpwnam(item->uidstr); + if (pwd) + uid = pwd->pw_uid; + else + ntfs_log_early_error("Invalid user \"%s\"\n", + item->uidstr); + } + } + /* + * Records with no uid and no gid are inserted + * to define the implicit mapping pattern + */ + if (uid + || (!item->uidstr[0] && !item->gidstr[0])) { + sid = encodesid(item->sidstr); + if (sid && ntfs_known_group_sid(sid)) { + ntfs_log_error("Bad user SID %s\n", + item->sidstr); + free(sid); + sid = (SID*)NULL; + } + if (sid && !item->uidstr[0] && !item->gidstr[0] + && !ntfs_valid_pattern(sid)) { + ntfs_log_error("Bad implicit SID pattern %s\n", + item->sidstr); + sid = (SID*)NULL; + } + if (sid) { + mapping = + (struct MAPPING*) + ntfs_malloc(sizeof(struct MAPPING)); + if (mapping) { + mapping->sid = sid; + mapping->xid = uid; + mapping->grcnt = 0; + mapping->next = (struct MAPPING*)NULL; + if (lastmapping) + lastmapping->next = mapping; + else + firstmapping = mapping; + lastmapping = mapping; + } + } + } + } + return (firstmapping); +} + +/* + * Build the group mapping list + * group identification may be given in symbolic or numeric format + * + * gid not associated to a uid are processed first in order + * to favour real groups + * + * ! Note ! : does getgrnam() read /etc/group or some other file ? + * if so there is a possible recursion into fuse if this + * file is on NTFS, and fuse is not recursion safe. + */ + +struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem) +{ + struct MAPLIST *item; + struct MAPPING *firstmapping; + struct MAPPING *lastmapping; + struct MAPPING *mapping; + struct group *grp; + BOOL secondstep; + BOOL ok; + int step; + SID *sid; + int gid; + + firstmapping = (struct MAPPING*)NULL; + lastmapping = (struct MAPPING*)NULL; + for (step=1; step<=2; step++) { + for (item = firstitem; item; item = item->next) { + secondstep = (item->uidstr[0] != '\0') + || !item->gidstr[0]; + ok = (step == 1 ? !secondstep : secondstep); + if ((item->gidstr[0] >= '0') + && (item->gidstr[0] <= '9')) + gid = atoi(item->gidstr); + else { + gid = 0; + if (item->gidstr[0]) { + grp = getgrnam(item->gidstr); + if (grp) + gid = grp->gr_gid; + else + ntfs_log_early_error("Invalid group \"%s\"\n", + item->gidstr); + } + } + /* + * Records with no uid and no gid are inserted in the + * second step to define the implicit mapping pattern + */ + if (ok + && (gid + || (!item->uidstr[0] && !item->gidstr[0]))) { + sid = encodesid(item->sidstr); + if (sid && !item->uidstr[0] && !item->gidstr[0] + && !ntfs_valid_pattern(sid)) { + /* error already logged */ + sid = (SID*)NULL; + } + if (sid) { + mapping = (struct MAPPING*) + ntfs_malloc(sizeof(struct MAPPING)); + if (mapping) { + mapping->sid = sid; + mapping->xid = gid; + /* special groups point to themselves */ + if (ntfs_known_group_sid(sid)) { + mapping->groups = + (gid_t*)&mapping->xid; + mapping->grcnt = 1; + } else + mapping->grcnt = 0; + + + mapping->next = (struct MAPPING*)NULL; + if (lastmapping) + lastmapping->next = mapping; + else + firstmapping = mapping; + lastmapping = mapping; + } + } + } + } + } + return (firstmapping); +} diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c new file mode 100755 index 0000000000000000000000000000000000000000..77b1f03ae3d7c35cadfabecaa353e6978822cf29 --- /dev/null +++ b/libntfs-3g/attrib.c @@ -0,0 +1,6886 @@ +/** + * attrib.c - Attribute handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2010 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2007-2014 Jean-Pierre Andre + * Copyright (c) 2010 Erik Larsson + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include "param.h" +#include "compat.h" +#include "attrib.h" +#include "attrlist.h" +#include "device.h" +#include "mft.h" +#include "debug.h" +#include "mst.h" +#include "volume.h" +#include "types.h" +#include "layout.h" +#include "inode.h" +#include "runlist.h" +#include "lcnalloc.h" +#include "dir.h" +#include "compress.h" +#include "bitmap.h" +#include "logging.h" +#include "misc.h" +#include "efs.h" + +ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') }; +ntfschar STREAM_SDS[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), + const_cpu_to_le16('D'), + const_cpu_to_le16('S'), + const_cpu_to_le16('\0') }; + +ntfschar TXF_DATA[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('T'), + const_cpu_to_le16('X'), + const_cpu_to_le16('F'), + const_cpu_to_le16('_'), + const_cpu_to_le16('D'), + const_cpu_to_le16('A'), + const_cpu_to_le16('T'), + const_cpu_to_le16('A'), + const_cpu_to_le16('\0') }; + +static int NAttrFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + return (na->ni->flags & flag); + return 0; +} + +static void NAttrSetFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + na->ni->flags |= flag; + else + ntfs_log_trace("Denied setting flag %d for not unnamed data " + "attribute\n", flag); +} + +static void NAttrClearFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +{ + if (na->type == AT_DATA && na->name == AT_UNNAMED) + na->ni->flags &= ~flag; +} + +#define GenNAttrIno(func_name, flag) \ +int NAttr##func_name(ntfs_attr *na) { return NAttrFlag (na, flag); } \ +void NAttrSet##func_name(ntfs_attr *na) { NAttrSetFlag (na, flag); } \ +void NAttrClear##func_name(ntfs_attr *na){ NAttrClearFlag(na, flag); } + +GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) +GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) +GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) + +/** + * ntfs_get_attribute_value_length - Find the length of an attribute + * @a: + * + * Description... + * + * Returns: + */ +s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a) +{ + if (!a) { + errno = EINVAL; + return 0; + } + errno = 0; + if (a->non_resident) + return sle64_to_cpu(a->data_size); + + return (s64)le32_to_cpu(a->value_length); +} + +/** + * ntfs_get_attribute_value - Get a copy of an attribute + * @vol: + * @a: + * @b: + * + * Description... + * + * Returns: + */ +s64 ntfs_get_attribute_value(const ntfs_volume *vol, + const ATTR_RECORD *a, u8 *b) +{ + runlist *rl; + s64 total, r; + int i; + + /* Sanity checks. */ + if (!vol || !a || !b) { + errno = EINVAL; + return 0; + } + /* Complex attribute? */ + /* + * Ignore the flags in case they are not zero for an attribute list + * attribute. Windows does not complain about invalid flags and chkdsk + * does not detect or fix them so we need to cope with it, too. + */ + if (a->type != AT_ATTRIBUTE_LIST && a->flags) { + ntfs_log_error("Non-zero (%04x) attribute flags. Cannot handle " + "this yet.\n", le16_to_cpu(a->flags)); + errno = EOPNOTSUPP; + return 0; + } + if (!a->non_resident) { + /* Attribute is resident. */ + + /* Sanity check. */ + if (le32_to_cpu(a->value_length) + le16_to_cpu(a->value_offset) + > le32_to_cpu(a->length)) { + return 0; + } + + memcpy(b, (const char*)a + le16_to_cpu(a->value_offset), + le32_to_cpu(a->value_length)); + errno = 0; + return (s64)le32_to_cpu(a->value_length); + } + + /* Attribute is not resident. */ + + /* If no data, return 0. */ + if (!(a->data_size)) { + errno = 0; + return 0; + } + /* + * FIXME: What about attribute lists?!? (AIA) + */ + /* Decompress the mapping pairs array into a runlist. */ + rl = ntfs_mapping_pairs_decompress(vol, a, NULL); + if (!rl) { + errno = EINVAL; + return 0; + } + /* + * FIXED: We were overflowing here in a nasty fashion when we + * reach the last cluster in the runlist as the buffer will + * only be big enough to hold data_size bytes while we are + * reading in allocated_size bytes which is usually larger + * than data_size, since the actual data is unlikely to have a + * size equal to a multiple of the cluster size! + * FIXED2: We were also overflowing here in the same fashion + * when the data_size was more than one run smaller than the + * allocated size which happens with Windows XP sometimes. + */ + /* Now load all clusters in the runlist into b. */ + for (i = 0, total = 0; rl[i].length; i++) { + if (total + (rl[i].length << vol->cluster_size_bits) >= + sle64_to_cpu(a->data_size)) { + unsigned char *intbuf = NULL; + /* + * We have reached the last run so we were going to + * overflow when executing the ntfs_pread() which is + * BAAAAAAAD! + * Temporary fix: + * Allocate a new buffer with size: + * rl[i].length << vol->cluster_size_bits, do the + * read into our buffer, then memcpy the correct + * amount of data into the caller supplied buffer, + * free our buffer, and continue. + * We have reached the end of data size so we were + * going to overflow in the same fashion. + * Temporary fix: same as above. + */ + intbuf = ntfs_malloc(rl[i].length << vol->cluster_size_bits); + if (!intbuf) { + free(rl); + return 0; + } + /* + * FIXME: If compressed file: Only read if lcn != -1. + * Otherwise, we are dealing with a sparse run and we + * just memset the user buffer to 0 for the length of + * the run, which should be 16 (= compression unit + * size). + * FIXME: Really only when file is compressed, or can + * we have sparse runs in uncompressed files as well? + * - Yes we can, in sparse files! But not necessarily + * size of 16, just run length. + */ + r = ntfs_pread(vol->dev, rl[i].lcn << + vol->cluster_size_bits, rl[i].length << + vol->cluster_size_bits, intbuf); + if (r != rl[i].length << vol->cluster_size_bits) { +#define ESTR "Error reading attribute value" + if (r == -1) + ntfs_log_perror(ESTR); + else if (r < rl[i].length << + vol->cluster_size_bits) { + ntfs_log_debug(ESTR ": Ran out of input data.\n"); + errno = EIO; + } else { + ntfs_log_debug(ESTR ": unknown error\n"); + errno = EIO; + } +#undef ESTR + free(rl); + free(intbuf); + return 0; + } + memcpy(b + total, intbuf, sle64_to_cpu(a->data_size) - + total); + free(intbuf); + total = sle64_to_cpu(a->data_size); + break; + } + /* + * FIXME: If compressed file: Only read if lcn != -1. + * Otherwise, we are dealing with a sparse run and we just + * memset the user buffer to 0 for the length of the run, which + * should be 16 (= compression unit size). + * FIXME: Really only when file is compressed, or can + * we have sparse runs in uncompressed files as well? + * - Yes we can, in sparse files! But not necessarily size of + * 16, just run length. + */ + r = ntfs_pread(vol->dev, rl[i].lcn << vol->cluster_size_bits, + rl[i].length << vol->cluster_size_bits, + b + total); + if (r != rl[i].length << vol->cluster_size_bits) { +#define ESTR "Error reading attribute value" + if (r == -1) + ntfs_log_perror(ESTR); + else if (r < rl[i].length << vol->cluster_size_bits) { + ntfs_log_debug(ESTR ": Ran out of input data.\n"); + errno = EIO; + } else { + ntfs_log_debug(ESTR ": unknown error\n"); + errno = EIO; + } +#undef ESTR + free(rl); + return 0; + } + total += r; + } + free(rl); + return total; +} + +/* Already cleaned up code below, but still look for FIXME:... */ + +/** + * __ntfs_attr_init - primary initialization of an ntfs attribute structure + * @na: ntfs attribute to initialize + * @ni: ntfs inode with which to initialize the ntfs attribute + * @type: attribute type + * @name: attribute name in little endian Unicode or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + * + * Initialize the ntfs attribute @na with @ni, @type, @name, and @name_len. + */ +static void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni, + const ATTR_TYPES type, ntfschar *name, const u32 name_len) +{ + na->rl = NULL; + na->ni = ni; + na->type = type; + na->name = name; + if (name) + na->name_len = name_len; + else + na->name_len = 0; +} + +/** + * ntfs_attr_init - initialize an ntfs_attr with data sizes and status + * @na: + * @non_resident: + * @compressed: + * @encrypted: + * @sparse: + * @allocated_size: + * @data_size: + * @initialized_size: + * @compressed_size: + * @compression_unit: + * + * Final initialization for an ntfs attribute. + */ +void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, + const ATTR_FLAGS data_flags, + const BOOL encrypted, const BOOL sparse, + const s64 allocated_size, const s64 data_size, + const s64 initialized_size, const s64 compressed_size, + const u8 compression_unit) +{ + if (!NAttrInitialized(na)) { + na->data_flags = data_flags; + if (non_resident) + NAttrSetNonResident(na); + if (data_flags & ATTR_COMPRESSION_MASK) + NAttrSetCompressed(na); + if (encrypted) + NAttrSetEncrypted(na); + if (sparse) + NAttrSetSparse(na); + na->allocated_size = allocated_size; + na->data_size = data_size; + na->initialized_size = initialized_size; + if ((data_flags & ATTR_COMPRESSION_MASK) || sparse) { + ntfs_volume *vol = na->ni->vol; + + na->compressed_size = compressed_size; + na->compression_block_clusters = 1 << compression_unit; + na->compression_block_size = 1 << (compression_unit + + vol->cluster_size_bits); + na->compression_block_size_bits = ffs( + na->compression_block_size) - 1; + } + NAttrSetInitialized(na); + } +} + +/** + * ntfs_attr_open - open an ntfs attribute for access + * @ni: open ntfs inode in which the ntfs attribute resides + * @type: attribute type + * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + * + * Allocate a new ntfs attribute structure, initialize it with @ni, @type, + * @name, and @name_len, then return it. Return NULL on error with + * errno set to the error code. + * + * If @name is AT_UNNAMED look specifically for an unnamed attribute. If you + * do not care whether the attribute is named or not set @name to NULL. In + * both those cases @name_len is not used at all. + */ +ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len) +{ + ntfs_attr_search_ctx *ctx; + ntfs_attr *na = NULL; + ntfschar *newname = NULL; + ATTR_RECORD *a; + le16 cs; + + ntfs_log_enter("Entering for inode %lld, attr 0x%x.\n", + (unsigned long long)ni->mft_no, type); + + if (!ni || !ni->vol || !ni->mrec) { + errno = EINVAL; + goto out; + } + na = ntfs_calloc(sizeof(ntfs_attr)); + if (!na) + goto out; + if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) { + name = ntfs_ucsndup(name, name_len); + if (!name) + goto err_out; + newname = name; + } + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + goto err_out; + + if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) + goto put_err_out; + + a = ctx->attr; + + if (!name) { + if (a->name_length) { + name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu( + a->name_offset)), a->name_length); + if (!name) + goto put_err_out; + newname = name; + name_len = a->name_length; + } else { + name = AT_UNNAMED; + name_len = 0; + } + } + + __ntfs_attr_init(na, ni, type, name, name_len); + + /* + * Wipe the flags in case they are not zero for an attribute list + * attribute. Windows does not complain about invalid flags and chkdsk + * does not detect or fix them so we need to cope with it, too. + */ + if (type == AT_ATTRIBUTE_LIST) + a->flags = 0; + + if ((type == AT_DATA) + && (a->non_resident ? !a->initialized_size : !a->value_length)) { + /* + * Define/redefine the compression state if stream is + * empty, based on the compression mark on parent + * directory (for unnamed data streams) or on current + * inode (for named data streams). The compression mark + * may change any time, the compression state can only + * change when stream is wiped out. + * + * Also prevent compression on NTFS version < 3.0 + * or cluster size > 4K or compression is disabled + */ + a->flags &= ~ATTR_COMPRESSION_MASK; + if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)) + a->flags |= ATTR_IS_COMPRESSED; + } + + cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); + + /* a file may be sparse though its unnamed data is not (cf $UsnJrnl) */ + if (na->type == AT_DATA && na->name == AT_UNNAMED && + (((a->flags & ATTR_IS_SPARSE) && !NAttrSparse(na)) || + (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) { + errno = EIO; + ntfs_log_perror("Inode %lld has corrupt attribute flags " + "(0x%x <> 0x%x)",(unsigned long long)ni->mft_no, + a->flags, na->ni->flags); + goto put_err_out; + } + + if (a->non_resident) { + if ((a->flags & ATTR_COMPRESSION_MASK) + && !a->compression_unit) { + errno = EIO; + ntfs_log_perror("Compressed inode %lld attr 0x%x has " + "no compression unit", + (unsigned long long)ni->mft_no, type); + goto put_err_out; + } + ntfs_attr_init(na, TRUE, a->flags, + a->flags & ATTR_IS_ENCRYPTED, + a->flags & ATTR_IS_SPARSE, + sle64_to_cpu(a->allocated_size), + sle64_to_cpu(a->data_size), + sle64_to_cpu(a->initialized_size), + cs ? sle64_to_cpu(a->compressed_size) : 0, + cs ? a->compression_unit : 0); + } else { + s64 l = le32_to_cpu(a->value_length); + ntfs_attr_init(na, FALSE, a->flags, + a->flags & ATTR_IS_ENCRYPTED, + a->flags & ATTR_IS_SPARSE, (l + 7) & ~7, l, l, + cs ? (l + 7) & ~7 : 0, 0); + } + ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); + return na; + +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + free(newname); + free(na); + na = NULL; + goto out; +} + +/** + * ntfs_attr_close - free an ntfs attribute structure + * @na: ntfs attribute structure to free + * + * Release all memory associated with the ntfs attribute @na and then release + * @na itself. + */ +void ntfs_attr_close(ntfs_attr *na) +{ + if (!na) + return; + if (NAttrNonResident(na) && na->rl) + free(na->rl); + /* Don't release if using an internal constant. */ + if (na->name != AT_UNNAMED && na->name != NTFS_INDEX_I30 + && na->name != STREAM_SDS) + free(na->name); + free(na); +} + +/** + * ntfs_attr_map_runlist - map (a part of) a runlist of an ntfs attribute + * @na: ntfs attribute for which to map (part of) a runlist + * @vcn: map runlist part containing this vcn + * + * Map the part of a runlist containing the @vcn of the ntfs attribute @na. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) +{ + LCN lcn; + ntfs_attr_search_ctx *ctx; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, (long long)vcn); + + lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); + if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) + return 0; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + + /* Find the attribute in the mft record. */ + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + vcn, NULL, 0, ctx)) { + runlist_element *rl; + + /* Decode the runlist. */ + rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr, + na->rl); + if (rl) { + na->rl = rl; + ntfs_attr_put_search_ctx(ctx); + return 0; + } + } + + ntfs_attr_put_search_ctx(ctx); + return -1; +} + +#if PARTIAL_RUNLIST_UPDATING + +/* + * Map the runlist of an attribute from some point to the end + * + * Returns 0 if success, + * -1 if it failed (errno telling why) + */ + +static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn) +{ + VCN last_vcn; + VCN highest_vcn; + VCN needed; + runlist_element *rl; + ATTR_RECORD *a; + BOOL startseen; + ntfs_attr_search_ctx *ctx; + BOOL done; + BOOL newrunlist; + + if (NAttrFullyMapped(na)) + return 0; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + + /* Get the last vcn in the attribute. */ + last_vcn = na->allocated_size >> na->ni->vol->cluster_size_bits; + + needed = vcn; + highest_vcn = 0; + startseen = FALSE; + done = FALSE; + rl = (runlist_element*)NULL; + do { + newrunlist = FALSE; + /* Find the attribute in the mft record. */ + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + needed, NULL, 0, ctx)) { + + a = ctx->attr; + /* Decode and merge the runlist. */ + if (ntfs_rl_vcn_to_lcn(na->rl, needed) + == LCN_RL_NOT_MAPPED) { + rl = ntfs_mapping_pairs_decompress(na->ni->vol, + a, na->rl); + newrunlist = TRUE; + } else + rl = na->rl; + if (rl) { + na->rl = rl; + highest_vcn = le64_to_cpu(a->highest_vcn); + if (highest_vcn < needed) { + /* corruption detection on unchanged runlists */ + if (newrunlist + && ((highest_vcn + 1) < last_vcn)) { + ntfs_log_error("Corrupt attribute list\n"); + rl = (runlist_element*)NULL; + errno = EIO; + } + done = TRUE; + } + needed = highest_vcn + 1; + if (!a->lowest_vcn) + startseen = TRUE; + } + } else { + done = TRUE; + } + } while (rl && !done && (needed < last_vcn)); + ntfs_attr_put_search_ctx(ctx); + /* + * Make sure we reached the end, unless the last + * runlist was modified earlier (using HOLES_DELAY + * leads to have a visibility over attributes which + * have not yet been fully updated) + */ + if (done && newrunlist && (needed < last_vcn)) { + ntfs_log_error("End of runlist not reached\n"); + rl = (runlist_element*)NULL; + errno = EIO; + } + /* mark fully mapped if we did so */ + if (rl && startseen) + NAttrSetFullyMapped(na); + return (rl ? 0 : -1); +} + +#endif + +/** + * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute + * @na: ntfs attribute for which to map the runlist + * + * Map the whole runlist of the ntfs attribute @na. For an attribute made up + * of only one attribute extent this is the same as calling + * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this + * will map the runlist fragments from each of the extents thus giving access + * to the entirety of the disk allocation of an attribute. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_map_whole_runlist(ntfs_attr *na) +{ + VCN next_vcn, last_vcn, highest_vcn; + ntfs_attr_search_ctx *ctx; + ntfs_volume *vol = na->ni->vol; + ATTR_RECORD *a; + int ret = -1; + int not_mapped; + + ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n", + (unsigned long long)na->ni->mft_no, na->type); + + /* avoid multiple full runlist mappings */ + if (NAttrFullyMapped(na)) { + ret = 0; + goto out; + } + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + goto out; + + /* Map all attribute extents one by one. */ + next_vcn = last_vcn = highest_vcn = 0; + a = NULL; + while (1) { + runlist_element *rl; + + not_mapped = 0; + if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED) + not_mapped = 1; + + if (ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, next_vcn, NULL, 0, ctx)) + break; + + a = ctx->attr; + + if (not_mapped) { + /* Decode the runlist. */ + rl = ntfs_mapping_pairs_decompress(na->ni->vol, + a, na->rl); + if (!rl) + goto err_out; + na->rl = rl; + } + + /* Are we in the first extent? */ + if (!next_vcn) { + if (a->lowest_vcn) { + errno = EIO; + ntfs_log_perror("First extent of inode %llu " + "attribute has non-zero lowest_vcn", + (unsigned long long)na->ni->mft_no); + goto err_out; + } + /* Get the last vcn in the attribute. */ + last_vcn = sle64_to_cpu(a->allocated_size) >> + vol->cluster_size_bits; + } + + /* Get the lowest vcn for the next extent. */ + highest_vcn = sle64_to_cpu(a->highest_vcn); + next_vcn = highest_vcn + 1; + + /* Only one extent or error, which we catch below. */ + if (next_vcn <= 0) { + errno = ENOENT; + break; + } + + /* Avoid endless loops due to corruption. */ + if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { + errno = EIO; + ntfs_log_perror("Inode %llu has corrupt attribute list", + (unsigned long long)na->ni->mft_no); + goto err_out; + } + } + if (!a) { + ntfs_log_perror("Couldn't find attribute for runlist mapping"); + goto err_out; + } + /* + * Cannot check highest_vcn when the last runlist has + * been modified earlier, as runlists and sizes may be + * updated without highest_vcn being in sync, when + * HOLES_DELAY is used + */ + if (not_mapped && highest_vcn && highest_vcn != last_vcn - 1) { + errno = EIO; + ntfs_log_perror("Failed to load full runlist: inode: %llu " + "highest_vcn: 0x%llx last_vcn: 0x%llx", + (unsigned long long)na->ni->mft_no, + (long long)highest_vcn, (long long)last_vcn); + goto err_out; + } + if (errno == ENOENT) { + NAttrSetFullyMapped(na); + ret = 0; + } +err_out: + ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_attr_vcn_to_lcn - convert a vcn into a lcn given an ntfs attribute + * @na: ntfs attribute whose runlist to use for conversion + * @vcn: vcn to convert + * + * Convert the virtual cluster number @vcn of an attribute into a logical + * cluster number (lcn) of a device using the runlist @na->rl to map vcns to + * their corresponding lcns. + * + * If the @vcn is not mapped yet, attempt to map the attribute extent + * containing the @vcn and retry the vcn to lcn conversion. + * + * Since lcns must be >= 0, we use negative return values with special meaning: + * + * Return value Meaning / Description + * ========================================== + * -1 = LCN_HOLE Hole / not allocated on disk. + * -3 = LCN_ENOENT There is no such vcn in the attribute. + * -4 = LCN_EINVAL Input parameter error. + * -5 = LCN_EIO Corrupt fs, disk i/o error, or not enough memory. + */ +LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn) +{ + LCN lcn; + BOOL is_retry = FALSE; + + if (!na || !NAttrNonResident(na) || vcn < 0) + return (LCN)LCN_EINVAL; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long + long)na->ni->mft_no, na->type); +retry: + /* Convert vcn to lcn. If that fails map the runlist and retry once. */ + lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); + if (lcn >= 0) + return lcn; + if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { + is_retry = TRUE; + goto retry; + } + /* + * If the attempt to map the runlist failed, or we are getting + * LCN_RL_NOT_MAPPED despite having mapped the attribute extent + * successfully, something is really badly wrong... + */ + if (!is_retry || lcn == (LCN)LCN_RL_NOT_MAPPED) + return (LCN)LCN_EIO; + /* lcn contains the appropriate error code. */ + return lcn; +} + +/** + * ntfs_attr_find_vcn - find a vcn in the runlist of an ntfs attribute + * @na: ntfs attribute whose runlist to search + * @vcn: vcn to find + * + * Find the virtual cluster number @vcn in the runlist of the ntfs attribute + * @na and return the the address of the runlist element containing the @vcn. + * + * Note you need to distinguish between the lcn of the returned runlist + * element being >= 0 and LCN_HOLE. In the later case you have to return zeroes + * on read and allocate clusters on write. You need to update the runlist, the + * attribute itself as well as write the modified mft record to disk. + * + * If there is an error return NULL with errno set to the error code. The + * following error codes are defined: + * EINVAL Input parameter error. + * ENOENT There is no such vcn in the runlist. + * ENOMEM Not enough memory. + * EIO I/O error or corrupt metadata. + */ +runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn) +{ + runlist_element *rl; + BOOL is_retry = FALSE; + + if (!na || !NAttrNonResident(na) || vcn < 0) { + errno = EINVAL; + return NULL; + } + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn %llx\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)vcn); +retry: + rl = na->rl; + if (!rl) + goto map_rl; + if (vcn < rl[0].vcn) + goto map_rl; + while (rl->length) { + if (vcn < rl[1].vcn) { + if (rl->lcn >= (LCN)LCN_HOLE) + return rl; + break; + } + rl++; + } + switch (rl->lcn) { + case (LCN)LCN_RL_NOT_MAPPED: + goto map_rl; + case (LCN)LCN_ENOENT: + errno = ENOENT; + break; + case (LCN)LCN_EINVAL: + errno = EINVAL; + break; + default: + errno = EIO; + break; + } + return NULL; +map_rl: + /* The @vcn is in an unmapped region, map the runlist and retry. */ + if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { + is_retry = TRUE; + goto retry; + } + /* + * If we already retried or the mapping attempt failed something has + * gone badly wrong. EINVAL and ENOENT coming from a failed mapping + * attempt are equivalent to errors for us as they should not happen + * in our code paths. + */ + if (is_retry || errno == EINVAL || errno == ENOENT) + errno = EIO; + return NULL; +} + +/** + * ntfs_attr_pread_i - see description at ntfs_attr_pread() + */ +static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b) +{ + s64 br, to_read, ofs, total, total2, max_read, max_init; + ntfs_volume *vol; + runlist_element *rl; + u16 efs_padding_length; + + /* Sanity checking arguments is done in ntfs_attr_pread(). */ + + if ((na->data_flags & ATTR_COMPRESSION_MASK) && NAttrNonResident(na)) { + if ((na->data_flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) + return ntfs_compressed_attr_pread(na, pos, count, b); + else { + /* compression mode not supported */ + errno = EOPNOTSUPP; + return -1; + } + } + /* + * Encrypted non-resident attributes are not supported. We return + * access denied, which is what Windows NT4 does, too. + * However, allow if mounted with efs_raw option + */ + vol = na->ni->vol; + if (!vol->efs_raw && NAttrEncrypted(na) && NAttrNonResident(na)) { + errno = EACCES; + return -1; + } + + if (!count) + return 0; + /* + * Truncate reads beyond end of attribute, + * but round to next 512 byte boundary for encrypted + * attributes with efs_raw mount option + */ + max_read = na->data_size; + max_init = na->initialized_size; + if (na->ni->vol->efs_raw + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) { + if (na->data_size != na->initialized_size) { + ntfs_log_error("uninitialized encrypted file not supported\n"); + errno = EINVAL; + return -1; + } + max_init = max_read = ((na->data_size + 511) & ~511) + 2; + } + if (pos + count > max_read) { + if (pos >= max_read) + return 0; + count = max_read - pos; + } + /* If it is a resident attribute, get the value from the mft record. */ + if (!NAttrNonResident(na)) { + ntfs_attr_search_ctx *ctx; + char *val; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, + 0, NULL, 0, ctx)) { +res_err_out: + ntfs_attr_put_search_ctx(ctx); + return -1; + } + val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); + if (val < (char*)ctx->attr || val + + le32_to_cpu(ctx->attr->value_length) > + (char*)ctx->mrec + vol->mft_record_size) { + errno = EIO; + ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); + goto res_err_out; + } + memcpy(b, val + pos, count); + ntfs_attr_put_search_ctx(ctx); + return count; + } + total = total2 = 0; + /* Zero out reads beyond initialized size. */ + if (pos + count > max_init) { + if (pos >= max_init) { + memset(b, 0, count); + return count; + } + total2 = pos + count - max_init; + count -= total2; + memset((u8*)b + count, 0, total2); + } + /* + * for encrypted non-resident attributes with efs_raw set + * the last two bytes aren't read from disk but contain + * the number of padding bytes so original size can be + * restored + */ + if (na->ni->vol->efs_raw && + (na->data_flags & ATTR_IS_ENCRYPTED) && + ((pos + count) > max_init-2)) { + efs_padding_length = 511 - ((na->data_size - 1) & 511); + if (pos+count == max_init) { + if (count == 1) { + *((u8*)b+count-1) = (u8)(efs_padding_length >> 8); + count--; + total2++; + } else { + *(u16*)((u8*)b+count-2) = cpu_to_le16(efs_padding_length); + count -= 2; + total2 +=2; + } + } else { + *((u8*)b+count-1) = (u8)(efs_padding_length & 0xff); + count--; + total2++; + } + } + + /* Find the runlist element containing the vcn. */ + rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); + if (!rl) { + /* + * If the vcn is not present it is an out of bounds read. + * However, we already truncated the read to the data_size, + * so getting this here is an error. + */ + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__); + } + return -1; + } + /* + * Gather the requested data into the linear destination buffer. Note, + * a partial final vcn is taken care of by the @count capping of read + * length. + */ + ofs = pos - (rl->vcn << vol->cluster_size_bits); + for (; count; rl++, ofs = 0) { + if (rl->lcn == LCN_RL_NOT_MAPPED) { + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl) { + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #2", + __FUNCTION__); + } + goto rl_err_out; + } + /* Needed for case when runs merged. */ + ofs = pos + total - (rl->vcn << vol->cluster_size_bits); + } + if (!rl->length) { + errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); + goto rl_err_out; + } + if (rl->lcn < (LCN)0) { + if (rl->lcn != (LCN)LCN_HOLE) { + ntfs_log_perror("%s: Bad run (%lld)", + __FUNCTION__, + (long long)rl->lcn); + goto rl_err_out; + } + /* It is a hole, just zero the matching @b range. */ + to_read = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + memset(b, 0, to_read); + /* Update progress counters. */ + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + continue; + } + /* It is a real lcn, read it into @dst. */ + to_read = min(count, (rl->length << vol->cluster_size_bits) - + ofs); +retry: + ntfs_log_trace("Reading %lld bytes from vcn %lld, lcn %lld, ofs" + " %lld.\n", (long long)to_read, (long long)rl->vcn, + (long long )rl->lcn, (long long)ofs); + br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + + ofs, to_read, b); + /* If everything ok, update progress counters and continue. */ + if (br > 0) { + total += br; + count -= br; + b = (u8*)b + br; + } + if (br == to_read) + continue; + /* If the syscall was interrupted, try again. */ + if (br == (s64)-1 && errno == EINTR) + goto retry; + if (total) + return total; + if (!br) + errno = EIO; + ntfs_log_perror("%s: ntfs_pread failed", __FUNCTION__); + return -1; + } + /* Finally, return the number of bytes read. */ + return total + total2; +rl_err_out: + if (total) + return total; + errno = EIO; + return -1; +} + +/** + * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer + * + * This function will read @count bytes starting at offset @pos from the ntfs + * attribute @na into the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that the read reached end of file or that an + * error was encountered during the read so that the read is partial. 0 means + * end of file or nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + */ +s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) +{ + s64 ret; + + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: na=%p b=%p pos=%lld count=%lld", + __FUNCTION__, na, b, (long long)pos, + (long long)count); + return -1; + } + + ntfs_log_enter("Entering for inode %lld attr 0x%x pos %lld count " + "%lld\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)pos, (long long)count); + + ret = ntfs_attr_pread_i(na, pos, count, b); + + ntfs_log_leave("\n"); + return ret; +} + +static int ntfs_attr_fill_zero(ntfs_attr *na, s64 pos, s64 count) +{ + char *buf; + s64 written, size, end = pos + count; + s64 ofsi; + const runlist_element *rli; + ntfs_volume *vol; + int ret = -1; + + ntfs_log_trace("pos %lld, count %lld\n", (long long)pos, + (long long)count); + + if (!na || pos < 0 || count < 0) { + errno = EINVAL; + goto err_out; + } + + buf = ntfs_calloc(NTFS_BUF_SIZE); + if (!buf) + goto err_out; + + rli = na->rl; + ofsi = 0; + vol = na->ni->vol; + while (pos < end) { + while (rli->length && (ofsi + (rli->length << + vol->cluster_size_bits) <= pos)) { + ofsi += (rli->length << vol->cluster_size_bits); + rli++; + } + size = min(end - pos, NTFS_BUF_SIZE); + /* + * If the zeroed block is fully within a hole, + * we need not write anything, so advance as far + * as possible within the hole. + */ + if ((rli->lcn == (LCN)LCN_HOLE) + && (ofsi <= pos) + && (ofsi + (rli->length << vol->cluster_size_bits) + >= (pos + size))) { + size = min(end - pos, ofsi - pos + + (rli->length << vol->cluster_size_bits)); + pos += size; + } else { + written = ntfs_rl_pwrite(vol, rli, ofsi, pos, + size, buf); + if (written <= 0) { + ntfs_log_perror("Failed to zero space"); + goto err_free; + } + pos += written; + } + } + + ret = 0; +err_free: + free(buf); +err_out: + return ret; +} + +static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, + runlist_element **rl, VCN *update_from) +{ + s64 to_write; + s64 need; + ntfs_volume *vol = na->ni->vol; + int eo, ret = -1; + runlist *rlc; + LCN lcn_seek_from = -1; + VCN cur_vcn, from_vcn; + + to_write = min(count, ((*rl)->length << vol->cluster_size_bits) - *ofs); + + cur_vcn = (*rl)->vcn; + from_vcn = (*rl)->vcn + (*ofs >> vol->cluster_size_bits); + + ntfs_log_trace("count: %lld, cur_vcn: %lld, from: %lld, to: %lld, ofs: " + "%lld\n", (long long)count, (long long)cur_vcn, + (long long)from_vcn, (long long)to_write, (long long)*ofs); + + /* Map the runlist to be able to update mapping pairs later. */ +#if PARTIAL_RUNLIST_UPDATING + if (!na->rl) { + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; + } else { + /* make sure the run ahead of hole is mapped */ + if ((*rl)->lcn == LCN_HOLE) { + if (ntfs_attr_map_partial_runlist(na, + (cur_vcn ? cur_vcn - 1 : cur_vcn))) + goto err_out; + } + } +#else + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; +#endif + + /* Restore @*rl, it probably get lost during runlist mapping. */ + *rl = ntfs_attr_find_vcn(na, cur_vcn); + if (!*rl) { + ntfs_log_error("Failed to find run after mapping runlist. " + "Please report to %s.\n", NTFS_DEV_LIST); + errno = EIO; + goto err_out; + } + + /* Search backwards to find the best lcn to start seek from. */ + rlc = *rl; + while (rlc->vcn) { + rlc--; + if (rlc->lcn >= 0) { + /* + * avoid fragmenting a compressed file + * Windows does not do that, and that may + * not be desirable for files which can + * be updated + */ + if (na->data_flags & ATTR_COMPRESSION_MASK) + lcn_seek_from = rlc->lcn + rlc->length; + else + lcn_seek_from = rlc->lcn + (from_vcn - rlc->vcn); + break; + } + } + if (lcn_seek_from == -1) { + /* Backwards search failed, search forwards. */ + rlc = *rl; + while (rlc->length) { + rlc++; + if (rlc->lcn >= 0) { + lcn_seek_from = rlc->lcn - (rlc->vcn - from_vcn); + if (lcn_seek_from < -1) + lcn_seek_from = -1; + break; + } + } + } + + need = ((*ofs + to_write - 1) >> vol->cluster_size_bits) + + 1 + (*rl)->vcn - from_vcn; + if ((na->data_flags & ATTR_COMPRESSION_MASK) + && (need < na->compression_block_clusters)) { + /* + * for a compressed file, be sure to allocate the full + * compression block, as we may need space to decompress + * existing compressed data. + * So allocate the space common to compression block + * and existing hole. + */ + VCN alloc_vcn; + + if ((from_vcn & -na->compression_block_clusters) <= (*rl)->vcn) + alloc_vcn = (*rl)->vcn; + else + alloc_vcn = from_vcn & -na->compression_block_clusters; + need = (alloc_vcn | (na->compression_block_clusters - 1)) + + 1 - alloc_vcn; + if (need > (*rl)->length) { + ntfs_log_error("Cannot allocate %lld clusters" + " within a hole of %lld\n", + (long long)need, + (long long)(*rl)->length); + errno = EIO; + goto err_out; + } + rlc = ntfs_cluster_alloc(vol, alloc_vcn, need, + lcn_seek_from, DATA_ZONE); + } else + rlc = ntfs_cluster_alloc(vol, from_vcn, need, + lcn_seek_from, DATA_ZONE); + if (!rlc) + goto err_out; + if (na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) + na->compressed_size += need << vol->cluster_size_bits; + + *rl = ntfs_runlists_merge(na->rl, rlc); + NAttrSetRunlistDirty(na); + /* + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. + */ + if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) { + runlist_element *oldrl = na->rl; + na->rl = *rl; + *rl = ntfs_rl_extend(na,*rl,2); + if (!*rl) na->rl = oldrl; /* restore to original if failed */ + } + if (!*rl) { + eo = errno; + ntfs_log_perror("Failed to merge runlists"); + if (ntfs_cluster_free_from_rl(vol, rlc)) { + ntfs_log_perror("Failed to free hot clusters. " + "Please run chkdsk /f"); + } + errno = eo; + goto err_out; + } + na->unused_runs = 2; + na->rl = *rl; + if ((*update_from == -1) || (from_vcn < *update_from)) + *update_from = from_vcn; + *rl = ntfs_attr_find_vcn(na, cur_vcn); + if (!*rl) { + /* + * It's definitely a BUG, if we failed to find @cur_vcn, because + * we missed it during instantiating of the hole. + */ + ntfs_log_error("Failed to find run after hole instantiation. " + "Please report to %s.\n", NTFS_DEV_LIST); + errno = EIO; + goto err_out; + } + /* If leaved part of the hole go to the next run. */ + if ((*rl)->lcn < 0) + (*rl)++; + /* Now LCN shoudn't be less than 0. */ + if ((*rl)->lcn < 0) { + ntfs_log_error("BUG! LCN is lesser than 0. " + "Please report to the %s.\n", NTFS_DEV_LIST); + errno = EIO; + goto err_out; + } + if (*ofs) { + /* Clear non-sparse region from @cur_vcn to @*ofs. */ + if (ntfs_attr_fill_zero(na, cur_vcn << vol->cluster_size_bits, + *ofs)) + goto err_out; + } + if ((*rl)->vcn < cur_vcn) { + /* + * Clusters that replaced hole are merged with + * previous run, so we need to update offset. + */ + *ofs += (cur_vcn - (*rl)->vcn) << vol->cluster_size_bits; + } + if ((*rl)->vcn > cur_vcn) { + /* + * We left part of the hole, so we need to update offset + */ + *ofs -= ((*rl)->vcn - cur_vcn) << vol->cluster_size_bits; + } + + ret = 0; +err_out: + return ret; +} + +static int stuff_hole(ntfs_attr *na, const s64 pos); + +/* + * Split an existing hole for overwriting with data + * The hole may have to be split into two or three parts, so + * that the overwritten part fits within a single compression block + * + * No cluster allocation is needed, this will be done later in + * standard hole filling, hence no need to reserve runs for + * future needs. + * + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * (or the full block, if it was a full hole) + * -1 if there were an error + */ + +static int split_compressed_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from) +{ + int compressed_part; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; + + compressed_part + = na->compression_block_clusters; + /* reserve entries in runlist if we have to split */ + if (rl->length > na->compression_block_clusters) { + *prl = ntfs_rl_extend(na,*prl,2); + if (!*prl) { + compressed_part = -1; + } else { + rl = *prl; + na->unused_runs = 2; + } + } + if (*prl && (rl->length > na->compression_block_clusters)) { + /* + * Locate the update part relative to beginning of + * current run + */ + int beginwrite = (pos >> cluster_size_bits) - rl->vcn; + s32 endblock = (((pos + count - 1) >> cluster_size_bits) + | (na->compression_block_clusters - 1)) + 1 - rl->vcn; + + compressed_part = na->compression_block_clusters + - (rl->length & (na->compression_block_clusters - 1)); + if ((beginwrite + compressed_part) >= na->compression_block_clusters) + compressed_part = na->compression_block_clusters; + /* + * if the run ends beyond end of needed block + * we have to split the run + */ + if (endblock < rl[0].length) { + runlist_element *xrl; + int n; + + /* + * we have to split into three parts if the run + * does not end within the first compression block. + * This means the hole begins before the + * compression block. + */ + if (endblock > na->compression_block_clusters) { + if (na->unused_runs < 2) { +ntfs_log_error("No free run, case 1\n"); + } + na->unused_runs -= 2; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[2] = *xrl; + xrl--; + } while (xrl != rl); + rl[1].length = na->compression_block_clusters; + rl[2].length = rl[0].length - endblock; + rl[0].length = endblock + - na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[2].lcn = LCN_HOLE; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[2].vcn = rl[1].vcn + + na->compression_block_clusters; + rl = ++(*prl); + } else { + /* + * split into two parts and use the + * first one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 2\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + if (beginwrite < endblock) { + /* we will write into the first part of hole */ + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + } else { + /* we will write into the second part of hole */ +// impossible ? + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + rl = ++(*prl); + } + } + } else { + if (rl[1].length) { + runlist_element *xrl; + int n; + + /* + * split into two parts and use the + * last one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 4\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + } else { + rl[2].lcn = rl[1].lcn; + rl[2].vcn = rl[1].vcn; + rl[2].length = rl[1].length; + } + rl[1].vcn -= na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[1].length = na->compression_block_clusters; + rl[0].length -= na->compression_block_clusters; + if (pos >= (rl[1].vcn << cluster_size_bits)) { + rl = ++(*prl); + } + } + NAttrSetRunlistDirty(na); + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + } + return (compressed_part); +} + +/* + * Borrow space from adjacent hole for appending data + * The hole may have to be split so that the end of hole is not + * affected by cluster allocation and overwriting + * Cluster allocation is needed for the overwritten compression block + * + * Must always leave two unused entries in the runlist + * + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * -1 if there were an error + */ + +static int borrow_from_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from, BOOL wasnonresident) +{ + int compressed_part = 0; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; + s32 endblock; + long long allocated; + runlist_element *zrl; + int irl; + BOOL undecided; + BOOL nothole; + + /* check whether the compression block is fully allocated */ + endblock = (((pos + count - 1) >> cluster_size_bits) | (na->compression_block_clusters - 1)) + 1 - rl->vcn; + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } + + undecided = (allocated < endblock) && (zrl->lcn == LCN_RL_NOT_MAPPED); + nothole = (allocated >= endblock) || (zrl->lcn != LCN_HOLE); + + if (undecided || nothole) { + runlist_element *orl = na->rl; + s64 olcn = (*prl)->lcn; +#if PARTIAL_RUNLIST_UPDATING + VCN prevblock; +#endif + /* + * Map the runlist, unless it has not been created. + * If appending data, a partial mapping from the + * end of previous block will do. + */ + irl = *prl - na->rl; +#if PARTIAL_RUNLIST_UPDATING + prevblock = pos >> cluster_size_bits; + if (prevblock) + prevblock--; + if (!NAttrBeingNonResident(na) + && (NAttrDataAppending(na) + ? ntfs_attr_map_partial_runlist(na,prevblock) + : ntfs_attr_map_whole_runlist(na))) { +#else + if (!NAttrBeingNonResident(na) + && ntfs_attr_map_whole_runlist(na)) { +#endif + rl = (runlist_element*)NULL; + } else { + /* + * Mapping the runlist may cause its relocation, + * and relocation may be at the same place with + * relocated contents. + * Have to find the current run again when this + * happens. + */ + if ((na->rl != orl) || ((*prl)->lcn != olcn)) { + zrl = &na->rl[irl]; + while (zrl->length && (zrl->lcn != olcn)) + zrl++; + *prl = zrl; + } + if (!(*prl)->length) { + ntfs_log_error("Mapped run not found," + " inode %lld lcn 0x%llx\n", + (long long)na->ni->mft_no, + (long long)olcn); + rl = (runlist_element*)NULL; + } else { + rl = ntfs_rl_extend(na,*prl,2); + na->unused_runs = 2; + } + } + *prl = rl; + if (rl && undecided) { + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) + && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } + } + } + /* + * compression block not fully allocated and followed + * by a hole : we must allocate in the hole. + */ + if (rl && (allocated < endblock) && (zrl->lcn == LCN_HOLE)) { + s64 xofs; + + /* + * split the hole if not fully needed + */ + if ((allocated + zrl->length) > endblock) { + runlist_element *xrl; + + *prl = ntfs_rl_extend(na,*prl,1); + if (*prl) { + /* beware : rl was reallocated */ + rl = *prl; + zrl = &rl[irl]; + na->unused_runs = 0; + xrl = zrl; + while (xrl->length) xrl++; + do { + xrl[1] = *xrl; + } while (xrl-- != zrl); + zrl->length = endblock - allocated; + zrl[1].length -= zrl->length; + zrl[1].vcn = zrl->vcn + zrl->length; + NAttrSetRunlistDirty(na); + } + } + if (*prl) { + if (wasnonresident) + compressed_part = na->compression_block_clusters + - zrl->length; + xofs = 0; + if (ntfs_attr_fill_hole(na, + zrl->length << cluster_size_bits, + &xofs, &zrl, update_from)) + compressed_part = -1; + else { + /* go back to initial cluster, now reallocated */ + while (zrl->vcn > (pos >> cluster_size_bits)) + zrl--; + *prl = zrl; + } + } + } + if (!*prl) { + ntfs_log_error("No elements to borrow from a hole\n"); + compressed_part = -1; + } else + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + return (compressed_part); +} + +static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, + hole_type holes); + +/** + * ntfs_attr_pwrite - positioned write to an ntfs attribute + * @na: ntfs attribute to write to + * @pos: position in the attribute to write to + * @count: number of bytes to write + * @b: data buffer to write to disk + * + * This function will write @count bytes from data buffer @b to ntfs attribute + * @na at position @pos. + * + * On success, return the number of successfully written bytes. If this number + * is lower than @count this means that an error was encountered during the + * write so that the write is partial. 0 means nothing was written (also return + * 0 when @count is 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of ntfs_pwrite(), or to EINVAL in case of + * invalid arguments. + */ +s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) +{ + s64 written, to_write, ofs, old_initialized_size, old_data_size; + s64 total = 0; + VCN update_from = -1; + ntfs_volume *vol; + s64 fullcount; + ntfs_attr_search_ctx *ctx = NULL; + runlist_element *rl; + s64 hole_end; + int eo; + int compressed_part; + struct { + unsigned int undo_initialized_size : 1; + unsigned int undo_data_size : 1; + } need_to = { 0, 0 }; + BOOL wasnonresident = FALSE; + BOOL compressed; + + ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count " + "0x%llx.\n", (long long)na->ni->mft_no, na->type, + (long long)pos, (long long)count); + + if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + goto errno_set; + } + vol = na->ni->vol; + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + na->unused_runs = 0; /* prepare overflow checks */ + /* + * Encrypted attributes are only supported in raw mode. We return + * access denied, which is what Windows NT4 does, too. + * Moreover a file cannot be both encrypted and compressed. + */ + if ((na->data_flags & ATTR_IS_ENCRYPTED) + && (compressed || !vol->efs_raw)) { + errno = EACCES; + goto errno_set; + } + /* + * Fill the gap, when writing beyond the end of a compressed + * file. This will make recursive calls + */ + if (compressed + && (na->type == AT_DATA) + && (pos > na->initialized_size) + && stuff_hole(na,pos)) + goto errno_set; + /* If this is a compressed attribute it needs special treatment. */ + wasnonresident = NAttrNonResident(na) != 0; + /* + * Compression is restricted to data streams and + * only ATTR_IS_COMPRESSED compression mode is supported. + */ + if (compressed + && ((na->type != AT_DATA) + || ((na->data_flags & ATTR_COMPRESSION_MASK) + != ATTR_IS_COMPRESSED))) { + errno = EOPNOTSUPP; + goto errno_set; + } + + if (!count) + goto out; + /* for a compressed file, get prepared to reserve a full block */ + fullcount = count; + /* If the write reaches beyond the end, extend the attribute. */ + old_data_size = na->data_size; + /* identify whether this is appending to a non resident data attribute */ + if ((na->type == AT_DATA) && (pos >= old_data_size) + && NAttrNonResident(na)) + NAttrSetDataAppending(na); + if (pos + count > na->data_size) { +#if PARTIAL_RUNLIST_UPDATING + /* + * When appending data, the attribute is first extended + * before being filled with data. This may cause the + * attribute to be made temporarily sparse, which + * implies reformating the inode and reorganizing the + * full runlist. To avoid unnecessary reorganization, + * we avoid sparse testing until the data is filled in. + */ + if (ntfs_attr_truncate_i(na, pos + count, + (NAttrDataAppending(na) ? + HOLES_DELAY : HOLES_OK))) { + ntfs_log_perror("Failed to enlarge attribute"); + goto errno_set; + } + /* + * If we avoided updating the runlist, we must be sure + * to cancel the enlargement and put back the runlist to + * a clean state if we get into some error. + */ + if (NAttrDataAppending(na)) + need_to.undo_data_size = 1; +#else + if (ntfs_attr_truncate_i(na, pos + count, HOLES_OK)) { + ntfs_log_perror("Failed to enlarge attribute"); + goto errno_set; + } +#endif + /* resizing may change the compression mode */ + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + need_to.undo_data_size = 1; + } + /* + * For compressed data, a single full block was allocated + * to deal with compression, possibly in a previous call. + * We are not able to process several blocks because + * some clusters are freed after compression and + * new allocations have to be done before proceeding, + * so truncate the requested count if needed (big buffers). + */ + if (compressed) { + fullcount = (pos | (na->compression_block_size - 1)) + 1 - pos; + if (count > fullcount) + count = fullcount; + } + old_initialized_size = na->initialized_size; + /* If it is a resident attribute, write the data to the mft record. */ + if (!NAttrNonResident(na)) { + char *val; + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + goto err_out; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_perror("%s: lookup failed", __FUNCTION__); + goto err_out; + } + val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); + if (val < (char*)ctx->attr || val + + le32_to_cpu(ctx->attr->value_length) > + (char*)ctx->mrec + vol->mft_record_size) { + errno = EIO; + ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); + goto err_out; + } + memcpy(val + pos, b, count); + if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, + ctx->mrec)) { + /* + * NOTE: We are in a bad state at this moment. We have + * dirtied the mft record but we failed to commit it to + * disk. Since we have read the mft record ok before, + * it is unlikely to fail writing it, so is ok to just + * return error here... (AIA) + */ + ntfs_log_perror("%s: failed to write mft record", __FUNCTION__); + goto err_out; + } + ntfs_attr_put_search_ctx(ctx); + total = count; + goto out; + } + + /* Handle writes beyond initialized_size. */ + + if (pos + count > na->initialized_size) { +#if PARTIAL_RUNLIST_UPDATING + /* + * When appending, we only need to map the end of the runlist, + * starting at the last previously allocated run, so that + * we are able a new one to it. + * However, for compressed file, we need the full compression + * block, which may be split in several extents. + */ + if (compressed && !NAttrDataAppending(na)) { + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; + } else { + VCN block_begin; + + if (NAttrDataAppending(na) + || (pos < na->initialized_size)) + block_begin = pos >> vol->cluster_size_bits; + else + block_begin = na->initialized_size >> vol->cluster_size_bits; + + if (compressed) + block_begin &= -na->compression_block_clusters; + if (block_begin) + block_begin--; + if (ntfs_attr_map_partial_runlist(na, block_begin)) + goto err_out; + if ((update_from == -1) || (block_begin < update_from)) + update_from = block_begin; + } +#else + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; +#endif + /* + * For a compressed attribute, we must be sure there is an + * available entry, and, when reopening a compressed file, + * we may need to split a hole. So reserve the entries + * before it gets too late. + */ + if (compressed) { + na->rl = ntfs_rl_extend(na,na->rl,2); + if (!na->rl) + goto err_out; + na->unused_runs = 2; + } + /* Set initialized_size to @pos + @count. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + goto err_out; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, + 0, NULL, 0, ctx)) + goto err_out; + + /* If write starts beyond initialized_size, zero the gap. */ + if (pos > na->initialized_size) + if (ntfs_attr_fill_zero(na, na->initialized_size, + pos - na->initialized_size)) + goto err_out; + + ctx->attr->initialized_size = cpu_to_sle64(pos + count); + /* fix data_size for compressed files */ + if (compressed) { + na->data_size = pos + count; + ctx->attr->data_size = ctx->attr->initialized_size; + } + if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, + ctx->mrec)) { + /* + * Undo the change in the in-memory copy and send it + * back for writing. + */ + ctx->attr->initialized_size = + cpu_to_sle64(old_initialized_size); + ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, + ctx->mrec); + goto err_out; + } + na->initialized_size = pos + count; +#if CACHE_NIDATA_SIZE + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + if ((compressed || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size = na->compressed_size; + else + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } +#endif + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + /* + * NOTE: At this point the initialized_size in the mft record + * has been updated BUT there is random data on disk thus if + * we decide to abort, we MUST change the initialized_size + * again. + */ + need_to.undo_initialized_size = 1; + } + /* Find the runlist element containing the vcn. */ + rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); + if (!rl) { + /* + * If the vcn is not present it is an out of bounds write. + * However, we already extended the size of the attribute, + * so getting this here must be an error of some kind. + */ + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #3", __FUNCTION__); + } + goto err_out; + } + /* + * Determine if there is compressed data in the current + * compression block (when appending to an existing file). + * If so, decompression will be needed, and the full block + * must be allocated to be identified as uncompressed. + * This comes in two variants, depending on whether + * compression has saved at least one cluster. + * The compressed size can never be over full size by + * more than 485 (maximum for 15 compression blocks + * compressed to 4098 and the last 3640 bytes compressed + * to 3640 + 3640/8 = 4095, with 15*2 + 4095 - 3640 = 485) + * This is less than the smallest cluster, so the hole is + * is never beyond the cluster next to the position of + * the first uncompressed byte to write. + */ + compressed_part = 0; + if (compressed) { + if ((rl->lcn == (LCN)LCN_HOLE) + && wasnonresident) { + if (rl->length < na->compression_block_clusters) + /* + * the needed block is in a hole smaller + * than the compression block : we can use + * it fully + */ + compressed_part + = na->compression_block_clusters + - rl->length; + else { + /* + * the needed block is in a hole bigger + * than the compression block : we must + * split the hole and use it partially + */ + compressed_part = split_compressed_hole(na, + &rl, pos, count, &update_from); + } + } else { + if (rl->lcn >= 0) { + /* + * the needed block contains data, make + * sure the full compression block is + * allocated. Borrow from hole if needed + */ + compressed_part = borrow_from_hole(na, + &rl, pos, count, &update_from, + wasnonresident); + } + } + + if (compressed_part < 0) + goto err_out; + + /* just making non-resident, so not yet compressed */ + if (NAttrBeingNonResident(na) + && (compressed_part < na->compression_block_clusters)) + compressed_part = 0; + } + ofs = pos - (rl->vcn << vol->cluster_size_bits); + /* + * Scatter the data from the linear data buffer to the volume. Note, a + * partial final vcn is taken care of by the @count capping of write + * length. + */ + for (hole_end = 0; count; rl++, ofs = 0) { + if (rl->lcn == LCN_RL_NOT_MAPPED) { + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl) { + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN" + " #4", __FUNCTION__); + } + goto rl_err_out; + } + /* Needed for case when runs merged. */ + ofs = pos + total - (rl->vcn << vol->cluster_size_bits); + } + if (!rl->length) { + errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); + goto rl_err_out; + } + if (rl->lcn < (LCN)0) { + hole_end = rl->vcn + rl->length; + + if (rl->lcn != (LCN)LCN_HOLE) { + errno = EIO; + ntfs_log_perror("%s: Unexpected LCN (%lld)", + __FUNCTION__, + (long long)rl->lcn); + goto rl_err_out; + } + if (ntfs_attr_fill_hole(na, fullcount, &ofs, &rl, + &update_from)) + goto err_out; + } + if (compressed) { + while (rl->length + && (ofs >= (rl->length << vol->cluster_size_bits))) { + ofs -= rl->length << vol->cluster_size_bits; + rl++; + } + } + + /* It is a real lcn, write it to the volume. */ + to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs); +retry: + ntfs_log_trace("Writing %lld bytes to vcn %lld, lcn %lld, ofs " + "%lld.\n", (long long)to_write, (long long)rl->vcn, + (long long)rl->lcn, (long long)ofs); + if (!NVolReadOnly(vol)) { + + s64 wpos = (rl->lcn << vol->cluster_size_bits) + ofs; + s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write; + u32 bsize = vol->cluster_size; + /* Byte size needed to zero fill a cluster */ + s64 rounding = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; + /** + * Zero fill to cluster boundary if we're writing at the + * end of the attribute or into an ex-sparse cluster. + * This will cause the kernel not to seek and read disk + * blocks during write(2) to fill the end of the buffer + * which increases write speed by 2-10 fold typically. + * + * This is done even for compressed files, because + * data is generally first written uncompressed. + */ + if (rounding && ((wend == na->initialized_size) || + (wend < (hole_end << vol->cluster_size_bits)))){ + + char *cb; + + rounding += to_write; + + cb = ntfs_malloc(rounding); + if (!cb) + goto err_out; + + memcpy(cb, b, to_write); + memset(cb + to_write, 0, rounding - to_write); + + if (compressed) { + written = ntfs_compressed_pwrite(na, + rl, wpos, ofs, to_write, + rounding, cb, compressed_part, + &update_from); + } else { + written = ntfs_pwrite(vol->dev, wpos, + rounding, cb); + if (written == rounding) + written = to_write; + } + + free(cb); + } else { + if (compressed) { + written = ntfs_compressed_pwrite(na, + rl, wpos, ofs, to_write, + to_write, b, compressed_part, + &update_from); + } else + written = ntfs_pwrite(vol->dev, wpos, + to_write, b); + } + } else + written = to_write; + /* If everything ok, update progress counters and continue. */ + if (written > 0) { + total += written; + count -= written; + fullcount -= written; + b = (const u8*)b + written; + } + if (written != to_write) { + /* Partial write cannot be dealt with, stop there */ + /* If the syscall was interrupted, try again. */ + if (written == (s64)-1 && errno == EINTR) + goto retry; + if (!written) + errno = EIO; + goto rl_err_out; + } + compressed_part = 0; + } +done: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* + * Update mapping pairs if needed. + * For a compressed file, we try to make a partial update + * of the mapping list. This makes a difference only if + * inode extents were needed. + */ + if (NAttrRunlistDirty(na)) { + if (ntfs_attr_update_mapping_pairs(na, + (update_from < 0 ? 0 : update_from))) { + /* + * FIXME: trying to recover by goto rl_err_out; + * could cause driver hang by infinite looping. + */ + total = -1; + goto out; + } + if (!wasnonresident) + NAttrClearBeingNonResident(na); + NAttrClearDataAppending(na); + } +out: + ntfs_log_leave("\n"); + return total; +rl_err_out: + eo = errno; + if (total) { + if (need_to.undo_initialized_size) { + if (pos + total > na->initialized_size) + goto done; + /* + * TODO: Need to try to change initialized_size. If it + * succeeds goto done, otherwise goto err_out. (AIA) + */ + goto err_out; + } + goto done; + } + errno = eo; +err_out: + eo = errno; + if (need_to.undo_initialized_size) { + int err; + + err = 0; + if (!ctx) { + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + err = 1; + } else + ntfs_attr_reinit_search_ctx(ctx); + if (!err) { + err = ntfs_attr_lookup(na->type, na->name, + na->name_len, 0, 0, NULL, 0, ctx); + if (!err) { + na->initialized_size = old_initialized_size; + ctx->attr->initialized_size = cpu_to_sle64( + old_initialized_size); + err = ntfs_mft_record_write(vol, + ctx->ntfs_ino->mft_no, + ctx->mrec); + } + } + if (err) { + /* + * FIXME: At this stage could try to recover by filling + * old_initialized_size -> new_initialized_size with + * data or at least zeroes. (AIA) + */ + ntfs_log_error("Eeek! Failed to recover from error. " + "Leaving metadata in inconsistent " + "state! Run chkdsk!\n"); + } + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* Update mapping pairs if needed. */ + if (NAttrRunlistDirty(na)) + ntfs_attr_update_mapping_pairs(na, 0); + /* Restore original data_size if needed. */ + if (need_to.undo_data_size + && ntfs_attr_truncate_i(na, old_data_size, HOLES_OK)) + ntfs_log_perror("Failed to restore data_size"); + errno = eo; +errno_set: + total = -1; + goto out; +} + +int ntfs_attr_pclose(ntfs_attr *na) +{ + s64 ofs; + int failed; + BOOL ok = TRUE; + VCN update_from = -1; + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx = NULL; + runlist_element *rl; + int eo; + int compressed_part; + BOOL compressed; + + ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x.\n", + na->ni->mft_no, na->type); + + if (!na || !na->ni || !na->ni->vol) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + goto errno_set; + } + vol = na->ni->vol; + na->unused_runs = 0; + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + /* + * Encrypted non-resident attributes are not supported. We return + * access denied, which is what Windows NT4 does, too. + */ + if (NAttrEncrypted(na) && NAttrNonResident(na)) { + errno = EACCES; + goto errno_set; + } + /* If this is not a compressed attribute get out */ + /* same if it is resident */ + if (!compressed || !NAttrNonResident(na)) + goto out; + + /* safety check : no recursion on close */ + if (NAttrComprClosing(na)) { + errno = EIO; + ntfs_log_error("Bad ntfs_attr_pclose" + " recursion on inode %lld\n", + (long long)na->ni->mft_no); + goto out; + } + NAttrSetComprClosing(na); + /* + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. + */ + if (ntfs_attr_map_whole_runlist(na)) + goto err_out; + na->rl = ntfs_rl_extend(na,na->rl,2); + if (!na->rl) + goto err_out; + na->unused_runs = 2; + /* Find the runlist element containing the terminal vcn. */ + rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits); + if (!rl) { + /* + * If the vcn is not present it is an out of bounds write. + * However, we have already written the last byte uncompressed, + * so getting this here must be an error of some kind. + */ + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN #5", __FUNCTION__); + } + goto err_out; + } + /* + * Scatter the data from the linear data buffer to the volume. Note, a + * partial final vcn is taken care of by the @count capping of write + * length. + */ + compressed_part = 0; + if (rl->lcn >= 0) { + runlist_element *xrl; + + xrl = rl; + do { + xrl++; + } while (xrl->lcn >= 0); + compressed_part = (-xrl->length) + & (na->compression_block_clusters - 1); + } else + if (rl->lcn == (LCN)LCN_HOLE) { + if (rl->length < na->compression_block_clusters) + compressed_part + = na->compression_block_clusters + - rl->length; + else + compressed_part + = na->compression_block_clusters; + } + /* done, if the last block set was compressed */ + if (compressed_part) + goto out; + + ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits); + + if (rl->lcn == LCN_RL_NOT_MAPPED) { + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl) { + if (errno == ENOENT) { + errno = EIO; + ntfs_log_perror("%s: Failed to find VCN" + " #6", __FUNCTION__); + } + goto rl_err_out; + } + /* Needed for case when runs merged. */ + ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits); + } + if (!rl->length) { + errno = EIO; + ntfs_log_perror("%s: Zero run length", __FUNCTION__); + goto rl_err_out; + } + if (rl->lcn < (LCN)0) { + if (rl->lcn != (LCN)LCN_HOLE) { + errno = EIO; + ntfs_log_perror("%s: Unexpected LCN (%lld)", + __FUNCTION__, + (long long)rl->lcn); + goto rl_err_out; + } + + if (ntfs_attr_fill_hole(na, (s64)0, &ofs, &rl, &update_from)) + goto err_out; + } + while (rl->length + && (ofs >= (rl->length << vol->cluster_size_bits))) { + ofs -= rl->length << vol->cluster_size_bits; + rl++; + } + +retry: + failed = 0; + if (update_from < 0) update_from = 0; + if (!NVolReadOnly(vol)) { + failed = ntfs_compressed_close(na, rl, ofs, &update_from); +#if CACHE_NIDATA_SIZE + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->compressed_size; + set_nino_flag(na->ni,KnownSize); + } +#endif + } + if (failed) { + /* If the syscall was interrupted, try again. */ + if (errno == EINTR) + goto retry; + else + goto rl_err_out; + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* Update mapping pairs if needed. */ + if (NAttrFullyMapped(na)) + if (ntfs_attr_update_mapping_pairs(na, update_from)) { + /* + * FIXME: trying to recover by goto rl_err_out; + * could cause driver hang by infinite looping. + */ + ok = FALSE; + goto out; + } +out: + NAttrClearComprClosing(na); + ntfs_log_leave("\n"); + return (!ok); +rl_err_out: + /* + * need not restore old sizes, only compressed_size + * can have changed. It has been set according to + * the current runlist while updating the mapping pairs, + * and must be kept consistent with the runlists. + */ +err_out: + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + /* Update mapping pairs if needed. */ + if (NAttrFullyMapped(na)) + ntfs_attr_update_mapping_pairs(na, 0); + errno = eo; +errno_set: + ok = FALSE; + goto out; +} + +/** + * ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read + * @na: multi sector transfer protected ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @bk_cnt: number of mst protected blocks to read + * @bk_size: size of each mst protected block in bytes + * @dst: output data buffer + * + * This function will read @bk_cnt blocks of size @bk_size bytes each starting + * at offset @pos from the ntfs attribute @na into the data buffer @b. + * + * On success, the multi sector transfer fixups are applied and the number of + * read blocks is returned. If this number is lower than @bk_cnt this means + * that the read has either reached end of attribute or that an error was + * encountered during the read so that the read is partial. 0 means end of + * attribute or nothing to read (also return 0 when @bk_cnt or @bk_size are 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_attr_pread() or to EINVAL in case of invalid + * arguments. + * + * NOTE: If an incomplete multi sector transfer is detected the magic is + * changed to BAAD but no error is returned, i.e. it is possible that any of + * the returned blocks have multi sector transfer errors. This should be + * detected by the caller by checking each block with is_baad_recordp(&block). + * The reasoning is that we want to fixup as many blocks as possible and we + * want to return even bad ones to the caller so, e.g. in case of ntfsck, the + * errors can be repaired. + */ +s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, + const u32 bk_size, void *dst) +{ + s64 br; + u8 *end; + BOOL warn; + + ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)pos); + if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + br = ntfs_attr_pread(na, pos, bk_cnt * bk_size, dst); + if (br <= 0) + return br; + br /= bk_size; + /* log errors unless silenced */ + warn = !na->ni || !na->ni->vol || !NVolNoFixupWarn(na->ni->vol); + for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst + + bk_size) + ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)dst, bk_size, warn); + /* Finally, return the number of blocks read. */ + return br; +} + +/** + * ntfs_attr_mst_pwrite - multi sector transfer protected ntfs attribute write + * @na: multi sector transfer protected ntfs attribute to write to + * @pos: position in the attribute to write to + * @bk_cnt: number of mst protected blocks to write + * @bk_size: size of each mst protected block in bytes + * @src: data buffer to write to disk + * + * This function will write @bk_cnt blocks of size @bk_size bytes each from + * data buffer @b to multi sector transfer (mst) protected ntfs attribute @na + * at position @pos. + * + * On success, return the number of successfully written blocks. If this number + * is lower than @bk_cnt this means that an error was encountered during the + * write so that the write is partial. 0 means nothing was written (also + * return 0 when @bk_cnt or @bk_size are 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of ntfs_attr_pwrite(), or to EINVAL in case + * of invalid arguments. + * + * NOTE: We mst protect the data, write it, then mst deprotect it using a quick + * deprotect algorithm (no checking). This saves us from making a copy before + * the write and at the same time causes the usn to be incremented in the + * buffer. This conceptually fits in better with the idea that cached data is + * always deprotected and protection is performed when the data is actually + * going to hit the disk and the cache is immediately deprotected again + * simulating an mst read on the written data. This way cache coherency is + * achieved. + */ +s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, + const u32 bk_size, void *src) +{ + s64 written, i; + + ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)pos); + if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { + errno = EINVAL; + return -1; + } + if (!bk_cnt) + return 0; + /* Prepare data for writing. */ + for (i = 0; i < bk_cnt; ++i) { + int err; + + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) + ((u8*)src + i * bk_size), bk_size); + if (err < 0) { + /* Abort write at this position. */ + ntfs_log_perror("%s #1", __FUNCTION__); + if (!i) + return err; + bk_cnt = i; + break; + } + } + /* Write the prepared data. */ + written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src); + if (written <= 0) { + ntfs_log_perror("%s: written=%lld", __FUNCTION__, + (long long)written); + } + /* Quickly deprotect the data again. */ + for (i = 0; i < bk_cnt; ++i) + ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)src + i * + bk_size)); + if (written <= 0) + return written; + /* Finally, return the number of complete blocks written. */ + return written / bk_size; +} + +/** + * ntfs_attr_find - find (next) attribute in mft record + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * You shouldn't need to call this function directly. Use lookup_attr() instead. + * + * ntfs_attr_find() takes a search context @ctx as parameter and searches the + * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an + * attribute of @type, optionally @name and @val. If found, ntfs_attr_find() + * returns 0 and @ctx->attr will point to the found attribute. + * + * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and + * @ctx->attr will point to the attribute before which the attribute being + * searched for would need to be inserted if such an action were to be desired. + * + * On actual error, ntfs_attr_find() returns -1 with errno set to the error + * code but not to ENOENT. In this case @ctx->attr is undefined and in + * particular do not rely on it not changing. + * + * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it + * is FALSE, the search begins after @ctx->attr. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to + * indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_find() will return the next attribute in the + * mft record @ctx->mrec. + * + * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT. + * AT_END is not a valid attribute, its length is zero for example, thus it is + * safer to return error instead of success in this case. This also allows us + * to interoperate cleanly with ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and + * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record + * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at + * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case + * sensitive. When @name is present, @name_len is the @name length in Unicode + * characters. + * + * If @name is not present (NULL), we assume that the unnamed attribute is + * being searched for. + * + * Finally, the resident attribute value @val is looked for, if present. + * If @val is not present (NULL), @val_len is ignored. + * + * ntfs_attr_find() only searches the specified mft record and it ignores the + * presence of an attribute list attribute (unless it is the one being searched + * for, obviously). If you need to take attribute lists into consideration, use + * ntfs_attr_lookup() instead (see below). This also means that you cannot use + * ntfs_attr_find() to search for extent records of non-resident attributes, as + * extents with lowest_vcn != 0 are usually described by the attribute list + * attribute only. - Note that it is possible that the first extent is only in + * the attribute list while the last extent is in the base mft record, so don't + * rely on being able to find the first extent in the base mft record. + * + * Warning: Never use @val when looking for attribute types which can be + * non-resident as this most likely will result in a crash! + */ +static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) +{ + ATTR_RECORD *a; + ntfs_volume *vol; + ntfschar *upcase; + u32 upcase_len; + + ntfs_log_trace("attribute type 0x%x.\n", type); + + if (ctx->ntfs_ino) { + vol = ctx->ntfs_ino->vol; + upcase = vol->upcase; + upcase_len = vol->upcase_len; + } else { + if (name && name != AT_UNNAMED) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + vol = NULL; + upcase = NULL; + upcase_len = 0; + } + /* + * Iterate over attributes in mft record starting at @ctx->attr, or the + * attribute following that, if @ctx->is_first is TRUE. + */ + if (ctx->is_first) { + a = ctx->attr; + ctx->is_first = FALSE; + } else + a = (ATTR_RECORD*)((char*)ctx->attr + + le32_to_cpu(ctx->attr->length)); + for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { + if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_allocated)) + break; + ctx->attr = a; + if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > + le32_to_cpu(type))) || + (a->type == AT_END)) { + errno = ENOENT; + return -1; + } + if (!a->length) + break; + /* If this is an enumeration return this attribute. */ + if (type == AT_UNUSED) + return 0; + if (a->type != type) + continue; + /* + * If @name is AT_UNNAMED we want an unnamed attribute. + * If @name is present, compare the two names. + * Otherwise, match any attribute. + */ + if (name == AT_UNNAMED) { + /* The search failed if the found attribute is named. */ + if (a->name_length) { + errno = ENOENT; + return -1; + } + } else { + register int rc; + if (name && ((rc = ntfs_names_full_collate(name, + name_len, (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, ic, + upcase, upcase_len)))) { + /* + * If @name collates before a->name, + * there is no matching attribute. + */ + if (rc < 0) { + errno = ENOENT; + return -1; + } + /* If the strings are not equal, continue search. */ + continue; + } + } + /* + * The names match or @name not present and attribute is + * unnamed. If no @val specified, we have found the attribute + * and are done. + */ + if (!val) + return 0; + /* @val is present; compare values. */ + else { + register int rc; + + rc = memcmp(val, (char*)a +le16_to_cpu(a->value_offset), + min(val_len, + le32_to_cpu(a->value_length))); + /* + * If @val collates before the current attribute's + * value, there is no matching attribute. + */ + if (!rc) { + register u32 avl; + avl = le32_to_cpu(a->value_length); + if (val_len == avl) + return 0; + if (val_len < avl) { + errno = ENOENT; + return -1; + } + } else if (rc < 0) { + errno = ENOENT; + return -1; + } + } + } + errno = EIO; + ntfs_log_perror("%s: Corrupt inode (%lld)", __FUNCTION__, + ctx->ntfs_ino ? (long long)ctx->ntfs_ino->mft_no : -1); + return -1; +} + +void ntfs_attr_name_free(char **name) +{ + if (*name) { + free(*name); + *name = NULL; + } +} + +char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len) +{ + char *name = NULL; + int name_len; + + name_len = ntfs_ucstombs(uname, uname_len, &name, 0); + if (name_len < 0) { + ntfs_log_perror("ntfs_ucstombs"); + return NULL; + + } else if (name_len > 0) + return name; + + ntfs_attr_name_free(&name); + return NULL; +} + +/** + * ntfs_external_attr_find - find an attribute in the attribute list of an inode + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * You shouldn't need to call this function directly. Use ntfs_attr_lookup() + * instead. + * + * Find an attribute by searching the attribute list for the corresponding + * attribute list entry. Having found the entry, map the mft record for read + * if the attribute is in a different mft record/inode, find the attribute in + * there and return it. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_external_attr_find() repeatedly until it returns -1 with errno set to + * ENOENT to indicate that there are no more entries. During the enumeration, + * each successful call of ntfs_external_attr_find() will return the next + * attribute described by the attribute list of the base mft record described + * by the search context @ctx. + * + * If @type is AT_END, seek to the end of the base mft record ignoring the + * attribute list completely and return -1 with errno set to ENOENT. AT_END is + * not a valid attribute, its length is zero for example, thus it is safer to + * return error instead of success in this case. + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * On first search @ctx->ntfs_ino must be the inode of the base mft record and + * @ctx must have been obtained from a call to ntfs_attr_get_search_ctx(). + * On subsequent calls, @ctx->ntfs_ino can be any extent inode, too + * (@ctx->base_ntfs_ino is then the base inode). + * + * After finishing with the attribute/mft record you need to call + * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any + * mapped extent inodes, etc). + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * On success, @ctx->attr is the found attribute, it is in mft record + * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this + * attribute with @ctx->base_* being the base mft record to which @ctx->attr + * belongs. + * + * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the + * attribute which collates just after the attribute being searched for in the + * base ntfs inode, i.e. if one wants to add the attribute to the mft record + * this is the correct place to insert it into, and if there is not enough + * space, the attribute should be placed in an extent mft record. + * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list + * at which the new attribute's attribute list entry should be inserted. The + * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. + * The only exception to this is when @type is AT_END, in which case + * @ctx->al_entry is set to NULL also (see above). + * + * The following error codes are defined: + * ENOENT Attribute not found, not an error as such. + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + */ +static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn, const u8 *val, const u32 val_len, + ntfs_attr_search_ctx *ctx) +{ + ntfs_inode *base_ni, *ni; + ntfs_volume *vol; + ATTR_LIST_ENTRY *al_entry, *next_al_entry; + u8 *al_start, *al_end; + ATTR_RECORD *a; + ntfschar *al_name; + u32 al_name_len; + BOOL is_first_search = FALSE; + + ni = ctx->ntfs_ino; + base_ni = ctx->base_ntfs_ino; + ntfs_log_trace("Entering for inode %lld, attribute type 0x%x.\n", + (unsigned long long)ni->mft_no, type); + if (!base_ni) { + /* First call happens with the base mft record. */ + base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino; + ctx->base_mrec = ctx->mrec; + } + if (ni == base_ni) + ctx->base_attr = ctx->attr; + if (type == AT_END) + goto not_found; + vol = base_ni->vol; + al_start = base_ni->attr_list; + al_end = al_start + base_ni->attr_list_size; + if (!ctx->al_entry) { + ctx->al_entry = (ATTR_LIST_ENTRY*)al_start; + is_first_search = TRUE; + } + /* + * Iterate over entries in attribute list starting at @ctx->al_entry, + * or the entry following that, if @ctx->is_first is TRUE. + */ + if (ctx->is_first) { + al_entry = ctx->al_entry; + ctx->is_first = FALSE; + /* + * If an enumeration and the first attribute is higher than + * the attribute list itself, need to return the attribute list + * attribute. + */ + if ((type == AT_UNUSED) && is_first_search && + le32_to_cpu(al_entry->type) > + le32_to_cpu(AT_ATTRIBUTE_LIST)) + goto find_attr_list_attr; + } else { + al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry + + le16_to_cpu(ctx->al_entry->length)); + /* + * If this is an enumeration and the attribute list attribute + * is the next one in the enumeration sequence, just return the + * attribute list attribute from the base mft record as it is + * not listed in the attribute list itself. + */ + if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) < + le32_to_cpu(AT_ATTRIBUTE_LIST) && + le32_to_cpu(al_entry->type) > + le32_to_cpu(AT_ATTRIBUTE_LIST)) { + int rc; +find_attr_list_attr: + + /* Check for bogus calls. */ + if (name || name_len || val || val_len || lowest_vcn) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + + /* We want the base record. */ + ctx->ntfs_ino = base_ni; + ctx->mrec = ctx->base_mrec; + ctx->is_first = TRUE; + /* Sanity checks are performed elsewhere. */ + ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + + /* Find the attribute list attribute. */ + rc = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0, + IGNORE_CASE, NULL, 0, ctx); + + /* + * Setup the search context so the correct + * attribute is returned next time round. + */ + ctx->al_entry = al_entry; + ctx->is_first = TRUE; + + /* Got it. Done. */ + if (!rc) + return 0; + + /* Error! If other than not found return it. */ + if (errno != ENOENT) + return rc; + + /* Not found?!? Absurd! */ + errno = EIO; + ntfs_log_error("Attribute list wasn't found"); + return -1; + } + } + for (;; al_entry = next_al_entry) { + /* Out of bounds check. */ + if ((u8*)al_entry < base_ni->attr_list || + (u8*)al_entry > al_end) + break; /* Inode is corrupt. */ + ctx->al_entry = al_entry; + /* Catch the end of the attribute list. */ + if ((u8*)al_entry == al_end) + goto not_found; + if (!al_entry->length) + break; + if ((u8*)al_entry + 6 > al_end || (u8*)al_entry + + le16_to_cpu(al_entry->length) > al_end) + break; + next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry + + le16_to_cpu(al_entry->length)); + if (type != AT_UNUSED) { + if (le32_to_cpu(al_entry->type) > le32_to_cpu(type)) + goto not_found; + if (type != al_entry->type) + continue; + } + al_name_len = al_entry->name_length; + al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset); + /* + * If !@type we want the attribute represented by this + * attribute list entry. + */ + if (type == AT_UNUSED) + goto is_enumeration; + /* + * If @name is AT_UNNAMED we want an unnamed attribute. + * If @name is present, compare the two names. + * Otherwise, match any attribute. + */ + if (name == AT_UNNAMED) { + if (al_name_len) + goto not_found; + } else { + int rc; + + if (name && ((rc = ntfs_names_full_collate(name, + name_len, al_name, al_name_len, ic, + vol->upcase, vol->upcase_len)))) { + + /* + * If @name collates before al_name, + * there is no matching attribute. + */ + if (rc < 0) + goto not_found; + /* If the strings are not equal, continue search. */ + continue; + } + } + /* + * The names match or @name not present and attribute is + * unnamed. Now check @lowest_vcn. Continue search if the + * next attribute list entry still fits @lowest_vcn. Otherwise + * we have reached the right one or the search has failed. + */ + if (lowest_vcn && (u8*)next_al_entry >= al_start && + (u8*)next_al_entry + 6 < al_end && + (u8*)next_al_entry + le16_to_cpu( + next_al_entry->length) <= al_end && + sle64_to_cpu(next_al_entry->lowest_vcn) <= + lowest_vcn && + next_al_entry->type == al_entry->type && + next_al_entry->name_length == al_name_len && + ntfs_names_are_equal((ntfschar*)((char*) + next_al_entry + + next_al_entry->name_offset), + next_al_entry->name_length, + al_name, al_name_len, CASE_SENSITIVE, + vol->upcase, vol->upcase_len)) + continue; +is_enumeration: + if (MREF_LE(al_entry->mft_reference) == ni->mft_no) { + if (MSEQNO_LE(al_entry->mft_reference) != + le16_to_cpu( + ni->mrec->sequence_number)) { + ntfs_log_error("Found stale mft reference in " + "attribute list!\n"); + break; + } + } else { /* Mft references do not match. */ + /* Do we want the base record back? */ + if (MREF_LE(al_entry->mft_reference) == + base_ni->mft_no) { + ni = ctx->ntfs_ino = base_ni; + ctx->mrec = ctx->base_mrec; + } else { + /* We want an extent record. */ + ni = ntfs_extent_inode_open(base_ni, + al_entry->mft_reference); + if (!ni) + break; + ctx->ntfs_ino = ni; + ctx->mrec = ni->mrec; + } + } + a = ctx->attr = (ATTR_RECORD*)((char*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + /* + * ctx->ntfs_ino, ctx->mrec, and ctx->attr now point to the + * mft record containing the attribute represented by the + * current al_entry. + * + * We could call into ntfs_attr_find() to find the right + * attribute in this mft record but this would be less + * efficient and not quite accurate as ntfs_attr_find() ignores + * the attribute instance numbers for example which become + * important when one plays with attribute lists. Also, because + * a proper match has been found in the attribute list entry + * above, the comparison can now be optimized. So it is worth + * re-implementing a simplified ntfs_attr_find() here. + * + * Use a manual loop so we can still use break and continue + * with the same meanings as above. + */ +do_next_attr_loop: + if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_allocated)) + break; + if (a->type == AT_END) + continue; + if (!a->length) + break; + if (al_entry->instance != a->instance) + goto do_next_attr; + /* + * If the type and/or the name are/is mismatched between the + * attribute list entry and the attribute record, there is + * corruption so we break and return error EIO. + */ + if (al_entry->type != a->type) + break; + if (!ntfs_names_are_equal((ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, al_name, + al_name_len, CASE_SENSITIVE, + vol->upcase, vol->upcase_len)) + break; + ctx->attr = a; + /* + * If no @val specified or @val specified and it matches, we + * have found it! Also, if !@type, it is an enumeration, so we + * want the current attribute. + */ + if ((type == AT_UNUSED) || !val || (!a->non_resident && + le32_to_cpu(a->value_length) == val_len && + !memcmp((char*)a + le16_to_cpu(a->value_offset), + val, val_len))) { + return 0; + } +do_next_attr: + /* Proceed to the next attribute in the current mft record. */ + a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); + goto do_next_attr_loop; + } + if (ni != base_ni) { + ctx->ntfs_ino = base_ni; + ctx->mrec = ctx->base_mrec; + ctx->attr = ctx->base_attr; + } + errno = EIO; + ntfs_log_perror("Inode is corrupt (%lld)", (long long)base_ni->mft_no); + return -1; +not_found: + /* + * If we were looking for AT_END or we were enumerating and reached the + * end, we reset the search context @ctx and use ntfs_attr_find() to + * seek to the end of the base mft record. + */ + if (type == AT_UNUSED || type == AT_END) { + ntfs_attr_reinit_search_ctx(ctx); + return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len, + ctx); + } + /* + * The attribute wasn't found. Before we return, we want to ensure + * @ctx->mrec and @ctx->attr indicate the position at which the + * attribute should be inserted in the base mft record. Since we also + * want to preserve @ctx->al_entry we cannot reinitialize the search + * context using ntfs_attr_reinit_search_ctx() as this would set + * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see + * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve + * @ctx->al_entry as the remaining fields (base_*) are identical to + * their non base_ counterparts and we cannot set @ctx->base_attr + * correctly yet as we do not know what @ctx->attr will be set to by + * the call to ntfs_attr_find() below. + */ + ctx->mrec = ctx->base_mrec; + ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + ctx->is_first = TRUE; + ctx->ntfs_ino = ctx->base_ntfs_ino; + ctx->base_ntfs_ino = NULL; + ctx->base_mrec = NULL; + ctx->base_attr = NULL; + /* + * In case there are multiple matches in the base mft record, need to + * keep enumerating until we get an attribute not found response (or + * another error), otherwise we would keep returning the same attribute + * over and over again and all programs using us for enumeration would + * lock up in a tight loop. + */ + { + int ret; + + do { + ret = ntfs_attr_find(type, name, name_len, ic, val, + val_len, ctx); + } while (!ret); + return ret; + } +} + +/** + * ntfs_attr_lookup - find an attribute in an ntfs inode + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must + * be the base mft record and @ctx must have been obtained from a call to + * ntfs_attr_get_search_ctx(). + * + * This function transparently handles attribute lists and @ctx is used to + * continue searches where they were left off at. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT + * to indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_lookup() will return the next attribute, with + * the current attribute being described by the search context @ctx. + * + * If @type is AT_END, seek to the end of the base mft record ignoring the + * attribute list completely and return -1 with errno set to ENOENT. AT_END is + * not a valid attribute, its length is zero for example, thus it is safer to + * return error instead of success in this case. It should never be needed to + * do this, but we implement the functionality because it allows for simpler + * code inside ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * After finishing with the attribute/mft record you need to call + * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any + * mapped extent inodes, etc). + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * On success, @ctx->attr is the found attribute, it is in mft record + * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this + * attribute with @ctx->base_* being the base mft record to which @ctx->attr + * belongs. If no attribute list attribute is present @ctx->al_entry and + * @ctx->base_* are NULL. + * + * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the + * attribute which collates just after the attribute being searched for in the + * base ntfs inode, i.e. if one wants to add the attribute to the mft record + * this is the correct place to insert it into, and if there is not enough + * space, the attribute should be placed in an extent mft record. + * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list + * at which the new attribute's attribute list entry should be inserted. The + * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. + * The only exception to this is when @type is AT_END, in which case + * @ctx->al_entry is set to NULL also (see above). + * + * + * The following error codes are defined: + * ENOENT Attribute not found, not an error as such. + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + */ +int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn, const u8 *val, const u32 val_len, + ntfs_attr_search_ctx *ctx) +{ + ntfs_volume *vol; + ntfs_inode *base_ni; + int ret = -1; + + ntfs_log_enter("Entering for attribute type 0x%x\n", type); + + if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED && + (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) || + !vol->upcase || !vol->upcase_len))) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + goto out; + } + + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) + ret = ntfs_attr_find(type, name, name_len, ic, val, val_len, ctx); + else + ret = ntfs_external_attr_find(type, name, name_len, ic, + lowest_vcn, val, val_len, ctx); +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_attr_position - find given or next attribute type in an ntfs inode + * @type: attribute type to start lookup + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute type in an ntfs inode or the next attribute which is not + * the AT_END attribute. Please see more details at ntfs_attr_lookup. + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * The following error codes are defined: + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + * ENOSPC No attribute was found after 'type', only AT_END. + */ +int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) +{ + if (ntfs_attr_lookup(type, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + return -1; + if (ctx->attr->type == AT_END) { + errno = ENOSPC; + return -1; + } + } + return 0; +} + +/** + * ntfs_attr_init_search_ctx - initialize an attribute search context + * @ctx: attribute search context to initialize + * @ni: ntfs inode with which to initialize the search context + * @mrec: mft record with which to initialize the search context + * + * Initialize the attribute search context @ctx with @ni and @mrec. + */ +static void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx, + ntfs_inode *ni, MFT_RECORD *mrec) +{ + if (!mrec) + mrec = ni->mrec; + ctx->mrec = mrec; + /* Sanity checks are performed elsewhere. */ + ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); + ctx->is_first = TRUE; + ctx->ntfs_ino = ni; + ctx->al_entry = NULL; + ctx->base_ntfs_ino = NULL; + ctx->base_mrec = NULL; + ctx->base_attr = NULL; +} + +/** + * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context + * @ctx: attribute search context to reinitialize + * + * Reinitialize the attribute search context @ctx. + * + * This is used when a search for a new attribute is being started to reset + * the search context to the beginning. + */ +void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx) +{ + if (!ctx->base_ntfs_ino) { + /* No attribute list. */ + ctx->is_first = TRUE; + /* Sanity checks are performed elsewhere. */ + ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + + le16_to_cpu(ctx->mrec->attrs_offset)); + /* + * This needs resetting due to ntfs_external_attr_find() which + * can leave it set despite having zeroed ctx->base_ntfs_ino. + */ + ctx->al_entry = NULL; + return; + } /* Attribute list. */ + ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec); + return; +} + +/** + * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context + * @ni: ntfs inode with which to initialize the search context + * @mrec: mft record with which to initialize the search context + * + * Allocate a new attribute search context, initialize it with @ni and @mrec, + * and return it. Return NULL on error with errno set. + * + * @mrec can be NULL, in which case the mft record is taken from @ni. + * + * Note: For low level utilities which know what they are doing we allow @ni to + * be NULL and @mrec to be set. Do NOT do this unless you understand the + * implications!!! For example it is no longer safe to call ntfs_attr_lookup(). + */ +ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) +{ + ntfs_attr_search_ctx *ctx; + + if (!ni && !mrec) { + errno = EINVAL; + ntfs_log_perror("NULL arguments"); + return NULL; + } + ctx = ntfs_malloc(sizeof(ntfs_attr_search_ctx)); + if (ctx) + ntfs_attr_init_search_ctx(ctx, ni, mrec); + return ctx; +} + +/** + * ntfs_attr_put_search_ctx - release an attribute search context + * @ctx: attribute search context to free + * + * Release the attribute search context @ctx. + */ +void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx) +{ + // NOTE: save errno if it could change and function stays void! + free(ctx); +} + +/** + * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to find + * + * Search for the attribute definition record corresponding to the attribute + * @type in the $AttrDef system file. + * + * Return the attribute type definition record if found and NULL if not found + * or an error occurred. On error the error code is stored in errno. The + * following error codes are defined: + * ENOENT - The attribute @type is not specified in $AttrDef. + * EINVAL - Invalid parameters (e.g. @vol is not valid). + */ +ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, + const ATTR_TYPES type) +{ + ATTR_DEF *ad; + + if (!vol || !vol->attrdef || !type) { + errno = EINVAL; + ntfs_log_perror("%s: type=%d", __FUNCTION__, type); + return NULL; + } + for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef < + vol->attrdef_len && ad->type; ++ad) { + /* We haven't found it yet, carry on searching. */ + if (le32_to_cpu(ad->type) < le32_to_cpu(type)) + continue; + /* We found the attribute; return it. */ + if (ad->type == type) + return ad; + /* We have gone too far already. No point in continuing. */ + break; + } + errno = ENOENT; + ntfs_log_perror("%s: type=%d", __FUNCTION__, type); + return NULL; +} + +/** + * ntfs_attr_size_bounds_check - check a size of an attribute type for validity + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * @size: size which to check + * + * Check whether the @size in bytes is valid for an attribute of @type on the + * ntfs volume @vol. This information is obtained from $AttrDef system file. + * + * Return 0 if valid and -1 if not valid or an error occurred. On error the + * error code is stored in errno. The following error codes are defined: + * ERANGE - @size is not valid for the attribute @type. + * ENOENT - The attribute @type is not specified in $AttrDef. + * EINVAL - Invalid parameters (e.g. @size is < 0 or @vol is not valid). + */ +int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, + const s64 size) +{ + ATTR_DEF *ad; + s64 min_size, max_size; + + if (size < 0) { + errno = EINVAL; + ntfs_log_perror("%s: size=%lld", __FUNCTION__, + (long long)size); + return -1; + } + + /* + * $ATTRIBUTE_LIST shouldn't be greater than 0x40000, otherwise + * Windows would crash. This is not listed in the AttrDef. + */ + if (type == AT_ATTRIBUTE_LIST && size > 0x40000) { + errno = ERANGE; + ntfs_log_perror("Too large attrlist (%lld)", (long long)size); + return -1; + } + + ad = ntfs_attr_find_in_attrdef(vol, type); + if (!ad) + return -1; + + min_size = sle64_to_cpu(ad->min_size); + max_size = sle64_to_cpu(ad->max_size); + + /* The $AttrDef generated by Windows specifies 2 as min_size for the + * volume name attribute, but in reality Windows sets it to 0 when + * clearing the volume name. If we want to be able to clear the volume + * name we must also accept 0 as min_size, despite the $AttrDef + * definition. */ + if(type == AT_VOLUME_NAME) + min_size = 0; + + if ((min_size && (size < min_size)) || + ((max_size > 0) && (size > max_size))) { + errno = ERANGE; + ntfs_log_perror("Attr type %d size check failed (min,size,max=" + "%lld,%lld,%lld)", type, (long long)min_size, + (long long)size, (long long)max_size); + return -1; + } + return 0; +} + +/** + * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type to check + * @name: attribute name to check + * @name_len: attribute name length + * + * Check whether the attribute of @type and @name with name length @name_len on + * the ntfs volume @vol is allowed to be non-resident. This information is + * obtained from $AttrDef system file and is augmented by rules imposed by + * Microsoft (e.g. see http://support.microsoft.com/kb/974729/). + * + * Return 0 if the attribute is allowed to be non-resident and -1 if not or an + * error occurred. On error the error code is stored in errno. The following + * error codes are defined: + * EPERM - The attribute is not allowed to be non-resident. + * ENOENT - The attribute @type is not specified in $AttrDef. + * EINVAL - Invalid parameters (e.g. @vol is not valid). + */ +static int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPES type, + const ntfschar *name, int name_len) +{ + ATTR_DEF *ad; + BOOL allowed; + + /* + * Microsoft has decreed that $LOGGED_UTILITY_STREAM attributes with a + * name of $TXF_DATA must be resident despite the entry for + * $LOGGED_UTILITY_STREAM in $AttrDef allowing them to be non-resident. + * Failure to obey this on the root directory mft record of a volume + * causes Windows Vista and later to see the volume as a RAW volume and + * thus cannot mount it at all. + */ + if ((type == AT_LOGGED_UTILITY_STREAM) + && name + && ntfs_names_are_equal(TXF_DATA, 9, name, name_len, + CASE_SENSITIVE, vol->upcase, vol->upcase_len)) + allowed = FALSE; + else { + /* Find the attribute definition record in $AttrDef. */ + ad = ntfs_attr_find_in_attrdef(vol, type); + if (!ad) + return -1; + /* Check the flags and return the result. */ + allowed = !(ad->flags & ATTR_DEF_RESIDENT); + } + if (!allowed) { + errno = EPERM; + ntfs_log_trace("Attribute can't be non-resident\n"); + return -1; + } + return 0; +} + +/** + * ntfs_attr_can_be_resident - check if an attribute can be resident + * @vol: ntfs volume to which the attribute belongs + * @type: attribute type which to check + * + * Check whether the attribute of @type on the ntfs volume @vol is allowed to + * be resident. This information is derived from our ntfs knowledge and may + * not be completely accurate, especially when user defined attributes are + * present. Basically we allow everything to be resident except for index + * allocation and extended attribute attributes. + * + * Return 0 if the attribute is allowed to be resident and -1 if not or an + * error occurred. On error the error code is stored in errno. The following + * error codes are defined: + * EPERM - The attribute is not allowed to be resident. + * EINVAL - Invalid parameters (e.g. @vol is not valid). + * + * Warning: In the system file $MFT the attribute $Bitmap must be non-resident + * otherwise windows will not boot (blue screen of death)! We cannot + * check for this here as we don't know which inode's $Bitmap is being + * asked about so the caller needs to special case this. + */ +int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type) +{ + if (!vol || !vol->attrdef || !type) { + errno = EINVAL; + return -1; + } + if (type != AT_INDEX_ALLOCATION) + return 0; + + ntfs_log_trace("Attribute can't be resident\n"); + errno = EPERM; + return -1; +} + +/** + * ntfs_make_room_for_attr - make room for an attribute inside an mft record + * @m: mft record + * @pos: position at which to make space + * @size: byte size to make available at this position + * + * @pos points to the attribute in front of which we want to make space. + * + * Return 0 on success or -1 on error. On error the error code is stored in + * errno. Possible error codes are: + * ENOSPC - There is not enough space available to complete operation. The + * caller has to make space before calling this. + * EINVAL - Input parameters were faulty. + */ +int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) +{ + u32 biu; + + ntfs_log_trace("Entering for pos 0x%d, size %u.\n", + (int)(pos - (u8*)m), (unsigned) size); + + /* Make size 8-byte alignment. */ + size = (size + 7) & ~7; + + /* Rigorous consistency checks. */ + if (!m || !pos || pos < (u8*)m) { + errno = EINVAL; + ntfs_log_perror("%s: pos=%p m=%p", __FUNCTION__, pos, m); + return -1; + } + /* The -8 is for the attribute terminator. */ + if (pos - (u8*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) { + errno = EINVAL; + return -1; + } + /* Nothing to do. */ + if (!size) + return 0; + + biu = le32_to_cpu(m->bytes_in_use); + /* Do we have enough space? */ + if (biu + size > le32_to_cpu(m->bytes_allocated) || + pos + size > (u8*)m + le32_to_cpu(m->bytes_allocated)) { + errno = ENOSPC; + ntfs_log_trace("No enough space in the MFT record\n"); + return -1; + } + /* Move everything after pos to pos + size. */ + memmove(pos + size, pos, biu - (pos - (u8*)m)); + /* Update mft record. */ + m->bytes_in_use = cpu_to_le32(biu + size); + return 0; +} + +/** + * ntfs_resident_attr_record_add - add resident attribute to inode + * @ni: opened ntfs inode to which MFT record add attribute + * @type: type of the new attribute + * @name: name of the new attribute + * @name_len: name length of the new attribute + * @val: value of the new attribute + * @size: size of new attribute (length of @val, if @val != NULL) + * @flags: flags of the new attribute + * + * Return offset to attribute from the beginning of the mft record on success + * and -1 on error. On error the error code is stored in errno. + * Possible error codes are: + * EINVAL - Invalid arguments passed to function. + * EEXIST - Attribute of such type and with same name already exists. + * EIO - I/O error occurred or damaged filesystem. + */ +int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + const ntfschar *name, u8 name_len, const u8 *val, + u32 size, ATTR_FLAGS data_flags) +{ + ntfs_attr_search_ctx *ctx; + u32 length; + ATTR_RECORD *a; + MFT_RECORD *m; + int err, offset; + ntfs_inode *base_ni; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n", + (long long) ni->mft_no, (unsigned) type, (unsigned) data_flags); + + if (!ni || (!name && name_len)) { + errno = EINVAL; + return -1; + } + + if (ntfs_attr_can_be_resident(ni->vol, type)) { + if (errno == EPERM) + ntfs_log_trace("Attribute can't be resident.\n"); + else + ntfs_log_trace("ntfs_attr_can_be_resident failed.\n"); + return -1; + } + + /* Locate place where record should be. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, val, size, + ctx)) { + err = EEXIST; + ntfs_log_trace("Attribute already present.\n"); + goto put_err_out; + } + if (errno != ENOENT) { + err = EIO; + goto put_err_out; + } + a = ctx->attr; + m = ctx->mrec; + + /* Make room for attribute. */ + length = offsetof(ATTR_RECORD, resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7) + + ((size + 7) & ~7); + if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { + err = errno; + ntfs_log_trace("Failed to make room for attribute.\n"); + goto put_err_out; + } + + /* Setup record fields. */ + offset = ((u8*)a - (u8*)m); + a->type = type; + a->length = cpu_to_le32(length); + a->non_resident = 0; + a->name_length = name_len; + a->name_offset = (name_len + ? cpu_to_le16(offsetof(ATTR_RECORD, resident_end)) + : const_cpu_to_le16(0)); + a->flags = data_flags; + a->instance = m->next_attr_instance; + a->value_length = cpu_to_le32(size); + a->value_offset = cpu_to_le16(length - ((size + 7) & ~7)); + if (val) + memcpy((u8*)a + le16_to_cpu(a->value_offset), val, size); + else + memset((u8*)a + le16_to_cpu(a->value_offset), 0, size); + if (type == AT_FILE_NAME) + a->resident_flags = RESIDENT_ATTR_IS_INDEXED; + else + a->resident_flags = 0; + if (name_len) + memcpy((u8*)a + le16_to_cpu(a->name_offset), + name, sizeof(ntfschar) * name_len); + m->next_attr_instance = + cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); + if (ni->nr_extents == -1) + base_ni = ni->base_ni; + else + base_ni = ni; + if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { + if (ntfs_attrlist_entry_add(ni, a)) { + err = errno; + ntfs_attr_record_resize(m, a, 0); + ntfs_log_trace("Failed add attribute entry to " + "ATTRIBUTE_LIST.\n"); + goto put_err_out; + } + } + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? type == AT_INDEX_ROOT && name == NTFS_INDEX_I30 + : type == AT_DATA && name == AT_UNNAMED) { + ni->data_size = size; + ni->allocated_size = (size + 7) & ~7; + set_nino_flag(ni,KnownSize); + } + ntfs_inode_mark_dirty(ni); + ntfs_attr_put_search_ctx(ctx); + return offset; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_non_resident_attr_record_add - add extent of non-resident attribute + * @ni: opened ntfs inode to which MFT record add attribute + * @type: type of the new attribute extent + * @name: name of the new attribute extent + * @name_len: name length of the new attribute extent + * @lowest_vcn: lowest vcn of the new attribute extent + * @dataruns_size: dataruns size of the new attribute extent + * @flags: flags of the new attribute extent + * + * Return offset to attribute from the beginning of the mft record on success + * and -1 on error. On error the error code is stored in errno. + * Possible error codes are: + * EINVAL - Invalid arguments passed to function. + * EEXIST - Attribute of such type, with same lowest vcn and with same + * name already exists. + * EIO - I/O error occurred or damaged filesystem. + */ +int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, + const ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, + ATTR_FLAGS flags) +{ + ntfs_attr_search_ctx *ctx; + u32 length; + ATTR_RECORD *a; + MFT_RECORD *m; + ntfs_inode *base_ni; + int err, offset; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld, " + "dataruns_size %d, flags 0x%x.\n", + (long long) ni->mft_no, (unsigned) type, + (long long) lowest_vcn, dataruns_size, (unsigned) flags); + + if (!ni || dataruns_size <= 0 || (!name && name_len)) { + errno = EINVAL; + return -1; + } + + if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) { + if (errno == EPERM) + ntfs_log_perror("Attribute can't be non resident"); + else + ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); + return -1; + } + + /* Locate place where record should be. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0, + ctx)) { + err = EEXIST; + ntfs_log_perror("Attribute 0x%x already present", type); + goto put_err_out; + } + if (errno != ENOENT) { + ntfs_log_perror("ntfs_attr_find failed"); + err = EIO; + goto put_err_out; + } + a = ctx->attr; + m = ctx->mrec; + + /* Make room for attribute. */ + dataruns_size = (dataruns_size + 7) & ~7; + length = offsetof(ATTR_RECORD, compressed_size) + ((sizeof(ntfschar) * + name_len + 7) & ~7) + dataruns_size + + ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? + sizeof(a->compressed_size) : 0); + if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { + err = errno; + ntfs_log_perror("Failed to make room for attribute"); + goto put_err_out; + } + + /* Setup record fields. */ + a->type = type; + a->length = cpu_to_le32(length); + a->non_resident = 1; + a->name_length = name_len; + a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, compressed_size) + + ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? + sizeof(a->compressed_size) : 0)); + a->flags = flags; + a->instance = m->next_attr_instance; + a->lowest_vcn = cpu_to_sle64(lowest_vcn); + a->mapping_pairs_offset = cpu_to_le16(length - dataruns_size); + a->compression_unit = (flags & ATTR_IS_COMPRESSED) + ? STANDARD_COMPRESSION_UNIT : 0; + /* If @lowest_vcn == 0, than setup empty attribute. */ + if (!lowest_vcn) { + a->highest_vcn = cpu_to_sle64(-1); + a->allocated_size = 0; + a->data_size = 0; + a->initialized_size = 0; + /* Set empty mapping pairs. */ + *((u8*)a + le16_to_cpu(a->mapping_pairs_offset)) = 0; + } + if (name_len) + memcpy((u8*)a + le16_to_cpu(a->name_offset), + name, sizeof(ntfschar) * name_len); + m->next_attr_instance = + cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); + if (ni->nr_extents == -1) + base_ni = ni->base_ni; + else + base_ni = ni; + if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { + if (ntfs_attrlist_entry_add(ni, a)) { + err = errno; + ntfs_log_perror("Failed add attr entry to attrlist"); + ntfs_attr_record_resize(m, a, 0); + goto put_err_out; + } + } + ntfs_inode_mark_dirty(ni); + /* + * Locate offset from start of the MFT record where new attribute is + * placed. We need relookup it, because record maybe moved during + * update of attribute list. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, + lowest_vcn, NULL, 0, ctx)) { + ntfs_log_perror("%s: attribute lookup failed", __FUNCTION__); + ntfs_attr_put_search_ctx(ctx); + return -1; + + } + offset = (u8*)ctx->attr - (u8*)ctx->mrec; + ntfs_attr_put_search_ctx(ctx); + return offset; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_attr_record_rm - remove attribute extent + * @ctx: search context describing the attribute which should be removed + * + * If this function succeed, user should reinit search context if he/she wants + * use it anymore. + * + * Return 0 on success and -1 on error. On error the error code is stored in + * errno. Possible error codes are: + * EINVAL - Invalid arguments passed to function. + * EIO - I/O error occurred or damaged filesystem. + */ +int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) +{ + ntfs_inode *base_ni, *ni; + ATTR_TYPES type; + + if (!ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr) { + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) ctx->ntfs_ino->mft_no, + (unsigned) le32_to_cpu(ctx->attr->type)); + type = ctx->attr->type; + ni = ctx->ntfs_ino; + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + + /* Remove attribute itself. */ + if (ntfs_attr_record_resize(ctx->mrec, ctx->attr, 0)) { + ntfs_log_trace("Couldn't remove attribute record. Bug or damaged MFT " + "record.\n"); + if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) + if (ntfs_attrlist_entry_add(ni, ctx->attr)) + ntfs_log_trace("Rollback failed. Leaving inconstant " + "metadata.\n"); + errno = EIO; + return -1; + } + ntfs_inode_mark_dirty(ni); + + /* + * Remove record from $ATTRIBUTE_LIST if present and we don't want + * delete $ATTRIBUTE_LIST itself. + */ + if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) { + if (ntfs_attrlist_entry_rm(ctx)) { + ntfs_log_trace("Couldn't delete record from " + "$ATTRIBUTE_LIST.\n"); + return -1; + } + } + + /* Post $ATTRIBUTE_LIST delete setup. */ + if (type == AT_ATTRIBUTE_LIST) { + if (NInoAttrList(base_ni) && base_ni->attr_list) + free(base_ni->attr_list); + base_ni->attr_list = NULL; + NInoClearAttrList(base_ni); + NInoAttrListClearDirty(base_ni); + } + + /* Free MFT record, if it doesn't contain attributes. */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) - + le16_to_cpu(ctx->mrec->attrs_offset) == 8) { + if (ntfs_mft_record_free(ni->vol, ni)) { + // FIXME: We need rollback here. + ntfs_log_trace("Couldn't free MFT record.\n"); + errno = EIO; + return -1; + } + /* Remove done if we freed base inode. */ + if (ni == base_ni) + return 0; + } + + if (type == AT_ATTRIBUTE_LIST || !NInoAttrList(base_ni)) + return 0; + + /* Remove attribute list if we don't need it any more. */ + if (!ntfs_attrlist_need(base_ni)) { + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* + * FIXME: Should we succeed here? Definitely something + * goes wrong because NInoAttrList(base_ni) returned + * that we have got attribute list. + */ + ntfs_log_trace("Couldn't find attribute list. Succeed " + "anyway.\n"); + return 0; + } + /* Deallocate clusters. */ + if (ctx->attr->non_resident) { + runlist *al_rl; + + al_rl = ntfs_mapping_pairs_decompress(base_ni->vol, + ctx->attr, NULL); + if (!al_rl) { + ntfs_log_trace("Couldn't decompress attribute list " + "runlist. Succeed anyway.\n"); + return 0; + } + if (ntfs_cluster_free_from_rl(base_ni->vol, al_rl)) { + ntfs_log_trace("Leaking clusters! Run chkdsk. " + "Couldn't free clusters from " + "attribute list runlist.\n"); + } + free(al_rl); + } + /* Remove attribute record itself. */ + if (ntfs_attr_record_rm(ctx)) { + /* + * FIXME: Should we succeed here? BTW, chkdsk doesn't + * complain if it find MFT record with attribute list, + * but without extents. + */ + ntfs_log_trace("Couldn't remove attribute list. Succeed " + "anyway.\n"); + return 0; + } + } + return 0; +} + +/** + * ntfs_attr_add - add attribute to inode + * @ni: opened ntfs inode to which add attribute + * @type: type of the new attribute + * @name: name in unicode of the new attribute + * @name_len: name length in unicode characters of the new attribute + * @val: value of new attribute + * @size: size of the new attribute / length of @val (if specified) + * + * @val should always be specified for always resident attributes (eg. FILE_NAME + * attribute), for attributes that can become non-resident @val can be NULL + * (eg. DATA attribute). @size can be specified even if @val is NULL, in this + * case data size will be equal to @size and initialized size will be equal + * to 0. + * + * If inode haven't got enough space to add attribute, add attribute to one of + * it extents, if no extents present or no one of them have enough space, than + * allocate new extent and add attribute to it. + * + * If on one of this steps attribute list is needed but not present, than it is + * added transparently to caller. So, this function should not be called with + * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call + * ntfs_inode_add_attrlist instead. + * + * On success return 0. On error return -1 with errno set to the error code. + */ +int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, const u8 *val, s64 size) +{ + u32 attr_rec_size; + int err, i, offset; + BOOL is_resident; + BOOL can_be_non_resident = FALSE; + ntfs_inode *attr_ni; + ntfs_attr *na; + ATTR_FLAGS data_flags; + + if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) { + errno = EINVAL; + ntfs_log_perror("%s: ni=%p size=%lld", __FUNCTION__, ni, + (long long)size); + return -1; + } + + ntfs_log_trace("Entering for inode %lld, attr %x, size %lld.\n", + (long long)ni->mft_no, type, (long long)size); + + if (ni->nr_extents == -1) + ni = ni->base_ni; + + /* Check the attribute type and the size. */ + if (ntfs_attr_size_bounds_check(ni->vol, type, size)) { + if (errno == ENOENT) + errno = EIO; + return -1; + } + + /* Sanity checks for always resident attributes. */ + if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) { + if (errno != EPERM) { + err = errno; + ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); + goto err_out; + } + /* @val is mandatory. */ + if (!val) { + errno = EINVAL; + ntfs_log_perror("val is mandatory for always resident " + "attributes"); + return -1; + } + if (size > ni->vol->mft_record_size) { + errno = ERANGE; + ntfs_log_perror("Attribute is too big"); + return -1; + } + } else + can_be_non_resident = TRUE; + + /* + * Determine resident or not will be new attribute. We add 8 to size in + * non resident case for mapping pairs. + */ + if (!ntfs_attr_can_be_resident(ni->vol, type)) { + is_resident = TRUE; + } else { + if (errno != EPERM) { + err = errno; + ntfs_log_perror("ntfs_attr_can_be_resident failed"); + goto err_out; + } + is_resident = FALSE; + } + /* Calculate attribute record size. */ + if (is_resident) + attr_rec_size = offsetof(ATTR_RECORD, resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7) + + ((size + 7) & ~7); + else + attr_rec_size = offsetof(ATTR_RECORD, non_resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7) + 8; + + /* + * If we have enough free space for the new attribute in the base MFT + * record, then add attribute to it. + */ + if (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use) >= attr_rec_size) { + attr_ni = ni; + goto add_attr_record; + } + + /* Try to add to extent inodes. */ + if (ntfs_inode_attach_all_extents(ni)) { + err = errno; + ntfs_log_perror("Failed to attach all extents to inode"); + goto err_out; + } + for (i = 0; i < ni->nr_extents; i++) { + attr_ni = ni->extent_nis[i]; + if (le32_to_cpu(attr_ni->mrec->bytes_allocated) - + le32_to_cpu(attr_ni->mrec->bytes_in_use) >= + attr_rec_size) + goto add_attr_record; + } + + /* There is no extent that contain enough space for new attribute. */ + if (!NInoAttrList(ni)) { + /* Add attribute list not present, add it and retry. */ + if (ntfs_inode_add_attrlist(ni)) { + err = errno; + ntfs_log_perror("Failed to add attribute list"); + goto err_out; + } + return ntfs_attr_add(ni, type, name, name_len, val, size); + } + /* Allocate new extent. */ + attr_ni = ntfs_mft_record_alloc(ni->vol, ni); + if (!attr_ni) { + err = errno; + ntfs_log_perror("Failed to allocate extent record"); + goto err_out; + } + +add_attr_record: + if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && ((type == AT_DATA) + || ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30)))) + data_flags = ATTR_IS_COMPRESSED; + else + data_flags = const_cpu_to_le16(0); + if (is_resident) { + /* Add resident attribute. */ + offset = ntfs_resident_attr_record_add(attr_ni, type, name, + name_len, val, size, data_flags); + if (offset < 0) { + if (errno == ENOSPC && can_be_non_resident) + goto add_non_resident; + err = errno; + ntfs_log_perror("Failed to add resident attribute"); + goto free_err_out; + } + return 0; + } + +add_non_resident: + /* Add non resident attribute. */ + offset = ntfs_non_resident_attr_record_add(attr_ni, type, name, + name_len, 0, 8, data_flags); + if (offset < 0) { + err = errno; + ntfs_log_perror("Failed to add non resident attribute"); + goto free_err_out; + } + + /* If @size == 0, we are done. */ + if (!size) + return 0; + + /* Open new attribute and resize it. */ + na = ntfs_attr_open(ni, type, name, name_len); + if (!na) { + err = errno; + ntfs_log_perror("Failed to open just added attribute"); + goto rm_attr_err_out; + } + /* Resize and set attribute value. */ + if (ntfs_attr_truncate_i(na, size, HOLES_OK) || + (val && (ntfs_attr_pwrite(na, 0, size, val) != size))) { + err = errno; + ntfs_log_perror("Failed to initialize just added attribute"); + if (ntfs_attr_rm(na)) + ntfs_log_perror("Failed to remove just added attribute"); + ntfs_attr_close(na); + goto err_out; + } + ntfs_attr_close(na); + return 0; + +rm_attr_err_out: + /* Remove just added attribute. */ + if (ntfs_attr_record_resize(attr_ni->mrec, + (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) + ntfs_log_perror("Failed to remove just added attribute #2"); +free_err_out: + /* Free MFT record, if it doesn't contain attributes. */ + if (le32_to_cpu(attr_ni->mrec->bytes_in_use) - + le16_to_cpu(attr_ni->mrec->attrs_offset) == 8) + if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) + ntfs_log_perror("Failed to free MFT record"); +err_out: + errno = err; + return -1; +} + +/* + * Change an attribute flag + */ + +int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, const ntfschar *name, + u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask) +{ + ntfs_attr_search_ctx *ctx; + int res; + + res = -1; + /* Search for designated attribute */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (!ntfs_attr_lookup(type, name, name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + /* do the requested change (all small endian le16) */ + ctx->attr->flags = (ctx->attr->flags & ~mask) + | (flags & mask); + NInoSetDirty(ni); + res = 0; + } + ntfs_attr_put_search_ctx(ctx); + } + return (res); +} + + +/** + * ntfs_attr_rm - remove attribute from ntfs inode + * @na: opened ntfs attribute to delete + * + * Remove attribute and all it's extents from ntfs inode. If attribute was non + * resident also free all clusters allocated by attribute. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_attr_rm(ntfs_attr *na) +{ + ntfs_attr_search_ctx *ctx; + int ret = 0; + + if (!na) { + ntfs_log_trace("Invalid arguments passed.\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) na->ni->mft_no, na->type); + + /* Free cluster allocation. */ + if (NAttrNonResident(na)) { + if (ntfs_attr_map_whole_runlist(na)) + return -1; + if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) { + ntfs_log_trace("Failed to free cluster allocation. Leaving " + "inconstant metadata.\n"); + ret = -1; + } + } + + /* Search for attribute extents and remove them all. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (ntfs_attr_record_rm(ctx)) { + ntfs_log_trace("Failed to remove attribute extent. Leaving " + "inconstant metadata.\n"); + ret = -1; + } + ntfs_attr_reinit_search_ctx(ctx); + } + ntfs_attr_put_search_ctx(ctx); + if (errno != ENOENT) { + ntfs_log_trace("Attribute lookup failed. Probably leaving inconstant " + "metadata.\n"); + ret = -1; + } + + return ret; +} + +/** + * ntfs_attr_record_resize - resize an attribute record + * @m: mft record containing attribute record + * @a: attribute record to resize + * @new_size: new size in bytes to which to resize the attribute record @a + * + * Resize the attribute record @a, i.e. the resident part of the attribute, in + * the mft record @m to @new_size bytes. + * + * Return 0 on success and -1 on error with errno set to the error code. + * The following error codes are defined: + * ENOSPC - Not enough space in the mft record @m to perform the resize. + * Note that on error no modifications have been performed whatsoever. + * + * Warning: If you make a record smaller without having copied all the data you + * are interested in the data may be overwritten! + */ +int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) +{ + u32 old_size, alloc_size, attr_size; + + old_size = le32_to_cpu(m->bytes_in_use); + alloc_size = le32_to_cpu(m->bytes_allocated); + attr_size = le32_to_cpu(a->length); + + ntfs_log_trace("Sizes: old=%u alloc=%u attr=%u new=%u\n", + (unsigned)old_size, (unsigned)alloc_size, + (unsigned)attr_size, (unsigned)new_size); + + /* Align to 8 bytes, just in case the caller hasn't. */ + new_size = (new_size + 7) & ~7; + + /* If the actual attribute length has changed, move things around. */ + if (new_size != attr_size) { + + u32 new_muse = old_size - attr_size + new_size; + + /* Not enough space in this mft record. */ + if (new_muse > alloc_size) { + errno = ENOSPC; + ntfs_log_trace("Not enough space in the MFT record " + "(%u > %u)\n", new_muse, alloc_size); + return -1; + } + + if (a->type == AT_INDEX_ROOT && new_size > attr_size && + new_muse + 120 > alloc_size && old_size + 120 <= alloc_size) { + errno = ENOSPC; + ntfs_log_trace("Too big INDEX_ROOT (%u > %u)\n", + new_muse, alloc_size); + return STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; + } + + /* Move attributes following @a to their new location. */ + memmove((u8 *)a + new_size, (u8 *)a + attr_size, + old_size - ((u8 *)a - (u8 *)m) - attr_size); + + /* Adjust @m to reflect the change in used space. */ + m->bytes_in_use = cpu_to_le32(new_muse); + + /* Adjust @a to reflect the new size. */ + if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length)) + a->length = cpu_to_le32(new_size); + } + return 0; +} + +/** + * ntfs_resident_attr_value_resize - resize the value of a resident attribute + * @m: mft record containing attribute record + * @a: attribute record whose value to resize + * @new_size: new size in bytes to which to resize the attribute value of @a + * + * Resize the value of the attribute @a in the mft record @m to @new_size bytes. + * If the value is made bigger, the newly "allocated" space is cleared. + * + * Return 0 on success and -1 on error with errno set to the error code. + * The following error codes are defined: + * ENOSPC - Not enough space in the mft record @m to perform the resize. + * Note that on error no modifications have been performed whatsoever. + */ +int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, + const u32 new_size) +{ + int ret; + + ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size); + + /* Resize the resident part of the attribute record. */ + if ((ret = ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) + + new_size + 7) & ~7)) < 0) + return ret; + /* + * If we made the attribute value bigger, clear the area between the + * old size and @new_size. + */ + if (new_size > le32_to_cpu(a->value_length)) + memset((u8*)a + le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length), 0, new_size - + le32_to_cpu(a->value_length)); + /* Finally update the length of the attribute value. */ + a->value_length = cpu_to_le32(new_size); + return 0; +} + +/** + * ntfs_attr_record_move_to - move attribute record to target inode + * @ctx: attribute search context describing the attribute record + * @ni: opened ntfs inode to which move attribute record + * + * If this function succeed, user should reinit search context if he/she wants + * use it anymore. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni) +{ + ntfs_attr_search_ctx *nctx; + ATTR_RECORD *a; + int err; + + if (!ctx || !ctx->attr || !ctx->ntfs_ino || !ni) { + ntfs_log_trace("Invalid arguments passed.\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for ctx->attr->type 0x%x, ctx->ntfs_ino->mft_no " + "0x%llx, ni->mft_no 0x%llx.\n", + (unsigned) le32_to_cpu(ctx->attr->type), + (long long) ctx->ntfs_ino->mft_no, + (long long) ni->mft_no); + + if (ctx->ntfs_ino == ni) + return 0; + + if (!ctx->al_entry) { + ntfs_log_trace("Inode should contain attribute list to use this " + "function.\n"); + errno = EINVAL; + return -1; + } + + /* Find place in MFT record where attribute will be moved. */ + a = ctx->attr; + nctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!nctx) + return -1; + + /* + * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for + * attribute in @ni->mrec, not any extent inode in case if @ni is base + * file record. + */ + if (!ntfs_attr_find(a->type, (ntfschar*)((u8*)a + le16_to_cpu( + a->name_offset)), a->name_length, CASE_SENSITIVE, NULL, + 0, nctx)) { + ntfs_log_trace("Attribute of such type, with same name already " + "present in this MFT record.\n"); + err = EEXIST; + goto put_err_out; + } + if (errno != ENOENT) { + err = errno; + ntfs_log_debug("Attribute lookup failed.\n"); + goto put_err_out; + } + + /* Make space and move attribute. */ + if (ntfs_make_room_for_attr(ni->mrec, (u8*) nctx->attr, + le32_to_cpu(a->length))) { + err = errno; + ntfs_log_trace("Couldn't make space for attribute.\n"); + goto put_err_out; + } + memcpy(nctx->attr, a, le32_to_cpu(a->length)); + nctx->attr->instance = nctx->mrec->next_attr_instance; + nctx->mrec->next_attr_instance = cpu_to_le16( + (le16_to_cpu(nctx->mrec->next_attr_instance) + 1) & 0xffff); + ntfs_attr_record_resize(ctx->mrec, a, 0); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_inode_mark_dirty(ni); + + /* Update attribute list. */ + ctx->al_entry->mft_reference = + MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); + ctx->al_entry->instance = nctx->attr->instance; + ntfs_attrlist_mark_dirty(ni); + + ntfs_attr_put_search_ctx(nctx); + return 0; +put_err_out: + ntfs_attr_put_search_ctx(nctx); + errno = err; + return -1; +} + +/** + * ntfs_attr_record_move_away - move away attribute record from it's mft record + * @ctx: attribute search context describing the attribute record + * @extra: minimum amount of free space in the new holder of record + * + * New attribute record holder must have free @extra bytes after moving + * attribute record to it. + * + * If this function succeed, user should reinit search context if he/she wants + * use it anymore. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra) +{ + ntfs_inode *base_ni, *ni; + MFT_RECORD *m; + int i; + + if (!ctx || !ctx->attr || !ctx->ntfs_ino || extra < 0) { + errno = EINVAL; + ntfs_log_perror("%s: ctx=%p ctx->attr=%p extra=%d", __FUNCTION__, + ctx, ctx ? ctx->attr : NULL, extra); + return -1; + } + + ntfs_log_trace("Entering for attr 0x%x, inode %llu\n", + (unsigned) le32_to_cpu(ctx->attr->type), + (unsigned long long)ctx->ntfs_ino->mft_no); + + if (ctx->ntfs_ino->nr_extents == -1) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + + if (!NInoAttrList(base_ni)) { + errno = EINVAL; + ntfs_log_perror("Inode %llu has no attrlist", + (unsigned long long)base_ni->mft_no); + return -1; + } + + if (ntfs_inode_attach_all_extents(ctx->ntfs_ino)) { + ntfs_log_perror("Couldn't attach extents, inode=%llu", + (unsigned long long)base_ni->mft_no); + return -1; + } + + /* Walk through all extents and try to move attribute to them. */ + for (i = 0; i < base_ni->nr_extents; i++) { + ni = base_ni->extent_nis[i]; + m = ni->mrec; + + if (ctx->ntfs_ino->mft_no == ni->mft_no) + continue; + + if (le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) < + le32_to_cpu(ctx->attr->length) + extra) + continue; + + /* + * ntfs_attr_record_move_to can fail if extent with other lowest + * VCN already present in inode we trying move record to. So, + * do not return error. + */ + if (!ntfs_attr_record_move_to(ctx, ni)) + return 0; + } + + /* + * Failed to move attribute to one of the current extents, so allocate + * new extent and move attribute to it. + */ + ni = ntfs_mft_record_alloc(base_ni->vol, base_ni); + if (!ni) { + ntfs_log_perror("Couldn't allocate MFT record"); + return -1; + } + if (ntfs_attr_record_move_to(ctx, ni)) { + ntfs_log_perror("Couldn't move attribute to MFT record"); + return -1; + } + return 0; +} + +/** + * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute + * @na: open ntfs attribute to make non-resident + * @ctx: ntfs search context describing the attribute + * + * Convert a resident ntfs attribute to a non-resident one. + * + * Return 0 on success and -1 on error with errno set to the error code. The + * following error codes are defined: + * EPERM - The attribute is not allowed to be non-resident. + * TODO: others... + * + * NOTE to self: No changes in the attribute list are required to move from + * a resident to a non-resident attribute. + * + * Warning: We do not set the inode dirty and we do not write out anything! + * We expect the caller to do this as this is a fairly low level + * function and it is likely there will be further changes made. + */ +int ntfs_attr_make_non_resident(ntfs_attr *na, + ntfs_attr_search_ctx *ctx) +{ + s64 new_allocated_size, bw; + ntfs_volume *vol = na->ni->vol; + ATTR_REC *a = ctx->attr; + runlist *rl; + int mp_size, mp_ofs, name_ofs, arec_size, err; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long + long)na->ni->mft_no, na->type); + + /* Some preliminary sanity checking. */ + if (NAttrNonResident(na)) { + ntfs_log_trace("Eeek! Trying to make non-resident attribute " + "non-resident. Aborting...\n"); + errno = EINVAL; + return -1; + } + + /* Check that the attribute is allowed to be non-resident. */ + if (ntfs_attr_can_be_non_resident(vol, na->type, na->name, na->name_len)) + return -1; + + new_allocated_size = (le32_to_cpu(a->value_length) + vol->cluster_size + - 1) & ~(vol->cluster_size - 1); + + if (new_allocated_size > 0) { + if ((a->flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) { + /* must allocate full compression blocks */ + new_allocated_size = ((new_allocated_size - 1) + | ((1L << (STANDARD_COMPRESSION_UNIT + + vol->cluster_size_bits)) - 1)) + 1; + } + /* Start by allocating clusters to hold the attribute value. */ + rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >> + vol->cluster_size_bits, -1, DATA_ZONE); + if (!rl) + return -1; + } else + rl = NULL; + /* + * Setup the in-memory attribute structure to be non-resident so that + * we can use ntfs_attr_pwrite(). + */ + NAttrSetNonResident(na); + NAttrSetBeingNonResident(na); + na->rl = rl; + na->allocated_size = new_allocated_size; + na->data_size = na->initialized_size = le32_to_cpu(a->value_length); + /* + * FIXME: For now just clear all of these as we don't support them when + * writing. + */ + NAttrClearSparse(na); + NAttrClearEncrypted(na); + if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) { + /* set compression writing parameters */ + na->compression_block_size + = 1 << (STANDARD_COMPRESSION_UNIT + vol->cluster_size_bits); + na->compression_block_clusters = 1 << STANDARD_COMPRESSION_UNIT; + } + + if (rl) { + /* Now copy the attribute value to the allocated cluster(s). */ + bw = ntfs_attr_pwrite(na, 0, le32_to_cpu(a->value_length), + (u8*)a + le16_to_cpu(a->value_offset)); + if (bw != le32_to_cpu(a->value_length)) { + err = errno; + ntfs_log_debug("Eeek! Failed to write out attribute value " + "(bw = %lli, errno = %i). " + "Aborting...\n", (long long)bw, err); + if (bw >= 0) + err = EIO; + goto cluster_free_err_out; + } + } + /* Determine the size of the mapping pairs array. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, INT_MAX); + if (mp_size < 0) { + err = errno; + ntfs_log_debug("Eeek! Failed to get size for mapping pairs array. " + "Aborting...\n"); + goto cluster_free_err_out; + } + /* Calculate new offsets for the name and the mapping pairs array. */ + if (na->ni->flags & FILE_ATTR_COMPRESSED) + name_ofs = (sizeof(ATTR_REC) + 7) & ~7; + else + name_ofs = (sizeof(ATTR_REC) - sizeof(a->compressed_size) + 7) & ~7; + mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; + /* + * Determine the size of the resident part of the non-resident + * attribute record. (Not compressed thus no compressed_size element + * present.) + */ + arec_size = (mp_ofs + mp_size + 7) & ~7; + + /* Resize the resident part of the attribute record. */ + if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { + err = errno; + goto cluster_free_err_out; + } + + /* + * Convert the resident part of the attribute record to describe a + * non-resident attribute. + */ + a->non_resident = 1; + + /* Move the attribute name if it exists and update the offset. */ + if (a->name_length) + memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + a->name_offset = cpu_to_le16(name_ofs); + + /* Setup the fields specific to non-resident attributes. */ + a->lowest_vcn = cpu_to_sle64(0); + a->highest_vcn = cpu_to_sle64((new_allocated_size - 1) >> + vol->cluster_size_bits); + + a->mapping_pairs_offset = cpu_to_le16(mp_ofs); + + /* + * Update the flags to match the in-memory ones. + * However cannot change the compression state if we had + * a fuse_file_info open with a mark for release. + * The decisions about compression can only be made when + * creating/recreating the stream, not when making non resident. + */ + a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED); + if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) { + /* support only ATTR_IS_COMPRESSED compression mode */ + a->compression_unit = STANDARD_COMPRESSION_UNIT; + a->compressed_size = const_cpu_to_le64(0); + } else { + a->compression_unit = 0; + a->flags &= ~ATTR_COMPRESSION_MASK; + na->data_flags = a->flags; + } + + memset(&a->reserved1, 0, sizeof(a->reserved1)); + + a->allocated_size = cpu_to_sle64(new_allocated_size); + a->data_size = a->initialized_size = cpu_to_sle64(na->data_size); + + /* Generate the mapping pairs array in the attribute record. */ + if (ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, arec_size - mp_ofs, + rl, 0, NULL) < 0) { + // FIXME: Eeek! We need rollback! (AIA) + ntfs_log_trace("Eeek! Failed to build mapping pairs. Leaving " + "corrupt attribute record on disk. In memory " + "runlist is still intact! Error code is %i. " + "FIXME: Need to rollback instead!\n", errno); + return -1; + } + + /* Done! */ + return 0; + +cluster_free_err_out: + if (rl && ntfs_cluster_free(vol, na, 0, -1) < 0) + ntfs_log_trace("Eeek! Failed to release allocated clusters in error " + "code path. Leaving inconsistent metadata...\n"); + NAttrClearNonResident(na); + NAttrClearFullyMapped(na); + na->allocated_size = na->data_size; + na->rl = NULL; + free(rl); + errno = err; + return -1; +} + + +static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); + +/** + * ntfs_resident_attr_resize - resize a resident, open ntfs attribute + * @na: resident ntfs attribute to resize + * @newsize: new size (in bytes) to which to resize the attribute + * + * Change the size of a resident, open ntfs attribute @na to @newsize bytes. + * Can also be used to force an attribute non-resident. In this case, the + * size cannot be changed. + * + * On success return 0 + * On error return values are: + * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT + * STATUS_ERROR - otherwise + * The following error codes are defined: + * ENOMEM - Not enough memory to complete operation. + * ERANGE - @newsize is not valid for the attribute type of @na. + * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. + */ +static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, + hole_type holes) +{ + ntfs_attr_search_ctx *ctx; + ntfs_volume *vol; + ntfs_inode *ni; + int err, ret = STATUS_ERROR; + + ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)newsize); + + /* Get the attribute record that needs modification. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, + ctx)) { + err = errno; + ntfs_log_perror("ntfs_attr_lookup failed"); + goto put_err_out; + } + vol = na->ni->vol; + /* + * Check the attribute type and the corresponding minimum and maximum + * sizes against @newsize and fail if @newsize is out of bounds. + */ + if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { + err = errno; + if (err == ENOENT) + err = EIO; + ntfs_log_perror("%s: bounds check failed", __FUNCTION__); + goto put_err_out; + } + /* + * If @newsize is bigger than the mft record we need to make the + * attribute non-resident if the attribute type supports it. If it is + * smaller we can go ahead and attempt the resize. + */ + if ((newsize < vol->mft_record_size) && (holes != HOLES_NONRES)) { + /* Perform the resize of the attribute record. */ + if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, + newsize))) { + /* Update attribute size everywhere. */ + na->data_size = na->initialized_size = newsize; + na->allocated_size = (newsize + 7) & ~7; + if ((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) + na->compressed_size = na->allocated_size; + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + if (((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size + = na->compressed_size; + else + na->ni->allocated_size + = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + if (na->type == AT_DATA) + NInoFileNameSetDirty(na->ni); + } + goto resize_done; + } + /* Prefer AT_INDEX_ALLOCATION instead of AT_ATTRIBUTE_LIST */ + if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { + err = errno; + goto put_err_out; + } + } + /* There is not enough space in the mft record to perform the resize. */ + + /* Make the attribute non-resident if possible. */ + if (!ntfs_attr_make_non_resident(na, ctx)) { + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + /* + * do not truncate when forcing non-resident, this + * could cause the attribute to be made resident again, + * so size changes are not allowed. + */ + if (holes == HOLES_NONRES) { + ret = 0; + if (newsize != na->data_size) { + ntfs_log_error("Cannot change size when" + " forcing non-resident\n"); + errno = EIO; + ret = STATUS_ERROR; + } + return (ret); + } + /* Resize non-resident attribute */ + return ntfs_attr_truncate_i(na, newsize, holes); + } else if (errno != ENOSPC && errno != EPERM) { + err = errno; + ntfs_log_perror("Failed to make attribute non-resident"); + goto put_err_out; + } + + /* Try to make other attributes non-resident and retry each time. */ + ntfs_attr_init_search_ctx(ctx, NULL, na->ni->mrec); + while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { + ntfs_attr *tna; + ATTR_RECORD *a; + + a = ctx->attr; + if (a->non_resident) + continue; + + /* + * Check out whether convert is reasonable. Assume that mapping + * pairs will take 8 bytes. + */ + if (le32_to_cpu(a->length) <= offsetof(ATTR_RECORD, + compressed_size) + ((a->name_length * + sizeof(ntfschar) + 7) & ~7) + 8) + continue; + + tna = ntfs_attr_open(na->ni, a->type, (ntfschar*)((u8*)a + + le16_to_cpu(a->name_offset)), a->name_length); + if (!tna) { + err = errno; + ntfs_log_perror("Couldn't open attribute"); + goto put_err_out; + } + if (ntfs_attr_make_non_resident(tna, ctx)) { + ntfs_attr_close(tna); + continue; + } + if ((tna->type == AT_DATA) && !tna->name_len) { + /* + * If we had to make the unnamed data attribute + * non-resident, propagate its new allocated size + * to all name attributes and directory indexes + */ + tna->ni->allocated_size = tna->allocated_size; + NInoFileNameSetDirty(tna->ni); + } + if (((tna->data_flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) + && ntfs_attr_pclose(tna)) { + err = errno; + ntfs_attr_close(tna); + goto put_err_out; + } + ntfs_inode_mark_dirty(tna->ni); + ntfs_attr_close(tna); + ntfs_attr_put_search_ctx(ctx); + return ntfs_resident_attr_resize_i(na, newsize, holes); + } + /* Check whether error occurred. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("%s: Attribute lookup failed 1", __FUNCTION__); + goto put_err_out; + } + + /* + * The standard information and attribute list attributes can't be + * moved out from the base MFT record, so try to move out others. + */ + if (na->type==AT_STANDARD_INFORMATION || na->type==AT_ATTRIBUTE_LIST) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD, + non_resident_end) + 8)) { + ntfs_log_perror("Could not free space in MFT record"); + return -1; + } + return ntfs_resident_attr_resize_i(na, newsize, holes); + } + + /* + * Move the attribute to a new mft record, creating an attribute list + * attribute or modifying it if it is already present. + */ + + /* Point search context back to attribute which we need resize. */ + ntfs_attr_init_search_ctx(ctx, na->ni, NULL); + if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + ntfs_log_perror("%s: Attribute lookup failed 2", __FUNCTION__); + err = errno; + goto put_err_out; + } + + /* + * Check whether attribute is already single in this MFT record. + * 8 added for the attribute terminator. + */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) == + le16_to_cpu(ctx->mrec->attrs_offset) + + le32_to_cpu(ctx->attr->length) + 8) { + err = ENOSPC; + ntfs_log_trace("MFT record is filled with one attribute\n"); + ret = STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; + goto put_err_out; + } + + /* Add attribute list if not present. */ + if (na->ni->nr_extents == -1) + ni = na->ni->base_ni; + else + ni = na->ni; + if (!NInoAttrList(ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(ni)) + return -1; + return ntfs_resident_attr_resize_i(na, newsize, holes); + } + /* Allocate new mft record. */ + ni = ntfs_mft_record_alloc(vol, ni); + if (!ni) { + err = errno; + ntfs_log_perror("Couldn't allocate new MFT record"); + goto put_err_out; + } + /* Move attribute to it. */ + if (ntfs_attr_record_move_to(ctx, ni)) { + err = errno; + ntfs_log_perror("Couldn't move attribute to new MFT record"); + goto put_err_out; + } + /* Update ntfs attribute. */ + if (na->ni->nr_extents == -1) + na->ni = ni; + + ntfs_attr_put_search_ctx(ctx); + /* Try to perform resize once again. */ + return ntfs_resident_attr_resize_i(na, newsize, holes); + +resize_done: + /* + * Set the inode (and its base inode if it exists) dirty so it is + * written out later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return 0; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return ret; +} + +static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_resident_attr_resize_i(na, newsize, HOLES_OK); + ntfs_log_leave("\n"); + return ret; +} + +/* + * Force an attribute to be made non-resident without + * changing its size. + * + * This is particularly needed when the attribute has no data, + * as the non-resident variant requires more space in the MFT + * record, and may imply expelling some other attribute. + * + * As a consequence the existing ntfs_attr_search_ctx's have to + * be closed or reinitialized. + * + * returns 0 if successful, + * < 0 if failed, with errno telling why + */ + +int ntfs_attr_force_non_resident(ntfs_attr *na) +{ + int res; + + res = ntfs_resident_attr_resize_i(na, na->data_size, HOLES_NONRES); + if (!res && !NAttrNonResident(na)) { + res = -1; + errno = EIO; + ntfs_log_error("Failed to force non-resident\n"); + } + return (res); +} + +/** + * ntfs_attr_make_resident - convert a non-resident to a resident attribute + * @na: open ntfs attribute to make resident + * @ctx: ntfs search context describing the attribute + * + * Convert a non-resident ntfs attribute to a resident one. + * + * Return 0 on success and -1 on error with errno set to the error code. The + * following error codes are defined: + * EINVAL - Invalid arguments passed. + * EPERM - The attribute is not allowed to be resident. + * EIO - I/O error, damaged inode or bug. + * ENOSPC - There is no enough space to perform conversion. + * EOPNOTSUPP - Requested conversion is not supported yet. + * + * Warning: We do not set the inode dirty and we do not write out anything! + * We expect the caller to do this as this is a fairly low level + * function and it is likely there will be further changes made. + */ +static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) +{ + ntfs_volume *vol = na->ni->vol; + ATTR_REC *a = ctx->attr; + int name_ofs, val_ofs, err = EIO; + s64 arec_size, bytes_read; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long + long)na->ni->mft_no, na->type); + + /* Should be called for the first extent of the attribute. */ + if (sle64_to_cpu(a->lowest_vcn)) { + ntfs_log_trace("Eeek! Should be called for the first extent of the " + "attribute. Aborting...\n"); + errno = EINVAL; + return -1; + } + + /* Some preliminary sanity checking. */ + if (!NAttrNonResident(na)) { + ntfs_log_trace("Eeek! Trying to make resident attribute resident. " + "Aborting...\n"); + errno = EINVAL; + return -1; + } + + /* Make sure this is not $MFT/$BITMAP or Windows will not boot! */ + if (na->type == AT_BITMAP && na->ni->mft_no == FILE_MFT) { + errno = EPERM; + return -1; + } + + /* Check that the attribute is allowed to be resident. */ + if (ntfs_attr_can_be_resident(vol, na->type)) + return -1; + + if (na->data_flags & ATTR_IS_ENCRYPTED) { + ntfs_log_trace("Making encrypted streams resident is not " + "implemented yet.\n"); + errno = EOPNOTSUPP; + return -1; + } + + /* Work out offsets into and size of the resident attribute. */ + name_ofs = 24; /* = sizeof(resident_ATTR_REC); */ + val_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; + arec_size = (val_ofs + na->data_size + 7) & ~7; + + /* Sanity check the size before we start modifying the attribute. */ + if (le32_to_cpu(ctx->mrec->bytes_in_use) - le32_to_cpu(a->length) + + arec_size > le32_to_cpu(ctx->mrec->bytes_allocated)) { + errno = ENOSPC; + ntfs_log_trace("Not enough space to make attribute resident\n"); + return -1; + } + + /* Read and cache the whole runlist if not already done. */ + if (ntfs_attr_map_whole_runlist(na)) + return -1; + + /* Move the attribute name if it exists and update the offset. */ + if (a->name_length) { + memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + } + a->name_offset = cpu_to_le16(name_ofs); + + /* Resize the resident part of the attribute record. */ + if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { + /* + * Bug, because ntfs_attr_record_resize should not fail (we + * already checked that attribute fits MFT record). + */ + ntfs_log_error("BUG! Failed to resize attribute record. " + "Please report to the %s. Aborting...\n", + NTFS_DEV_LIST); + errno = EIO; + return -1; + } + + /* Convert the attribute record to describe a resident attribute. */ + a->non_resident = 0; + a->flags = 0; + a->value_length = cpu_to_le32(na->data_size); + a->value_offset = cpu_to_le16(val_ofs); + /* + * If a data stream was wiped out, adjust the compression mode + * to current state of compression flag + */ + if (!na->data_size + && (na->type == AT_DATA) + && (na->ni->vol->major_ver >= 3) + && NVolCompression(na->ni->vol) + && (na->ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && (na->ni->flags & FILE_ATTR_COMPRESSED)) { + a->flags |= ATTR_IS_COMPRESSED; + na->data_flags = a->flags; + } + /* + * File names cannot be non-resident so we would never see this here + * but at least it serves as a reminder that there may be attributes + * for which we do need to set this flag. (AIA) + */ + if (a->type == AT_FILE_NAME) + a->resident_flags = RESIDENT_ATTR_IS_INDEXED; + else + a->resident_flags = 0; + a->reservedR = 0; + + /* Sanity fixup... Shouldn't really happen. (AIA) */ + if (na->initialized_size > na->data_size) + na->initialized_size = na->data_size; + + /* Copy data from run list to resident attribute value. */ + bytes_read = ntfs_rl_pread(vol, na->rl, 0, na->initialized_size, + (u8*)a + val_ofs); + if (bytes_read != na->initialized_size) { + if (bytes_read < 0) + err = errno; + ntfs_log_trace("Eeek! Failed to read attribute data. Leaving " + "inconstant metadata. Run chkdsk. " + "Aborting...\n"); + errno = err; + return -1; + } + + /* Clear memory in gap between initialized_size and data_size. */ + if (na->initialized_size < na->data_size) + memset((u8*)a + val_ofs + na->initialized_size, 0, + na->data_size - na->initialized_size); + + /* + * Deallocate clusters from the runlist. + * + * NOTE: We can use ntfs_cluster_free() because we have already mapped + * the whole run list and thus it doesn't matter that the attribute + * record is in a transiently corrupted state at this moment in time. + */ + if (ntfs_cluster_free(vol, na, 0, -1) < 0) { + ntfs_log_perror("Eeek! Failed to release allocated clusters"); + ntfs_log_trace("Ignoring error and leaving behind wasted " + "clusters.\n"); + } + + /* Throw away the now unused runlist. */ + free(na->rl); + na->rl = NULL; + + /* Update in-memory struct ntfs_attr. */ + NAttrClearNonResident(na); + NAttrClearFullyMapped(na); + NAttrClearSparse(na); + NAttrClearEncrypted(na); + na->initialized_size = na->data_size; + na->allocated_size = na->compressed_size = (na->data_size + 7) & ~7; + na->compression_block_size = 0; + na->compression_block_size_bits = na->compression_block_clusters = 0; + return 0; +} + +/* + * If we are in the first extent, then set/clean sparse bit, + * update allocated and compressed size. + */ +static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, + hole_type holes, ntfs_attr_search_ctx *ctx) +{ + int sparse, ret = 0; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x\n", + (unsigned long long)na->ni->mft_no, na->type); + + if (a->lowest_vcn) + goto out; + + a->allocated_size = cpu_to_sle64(na->allocated_size); + + /* Update sparse bit, unless this is an intermediate state */ + if (holes == HOLES_DELAY) + sparse = (a->flags & ATTR_IS_SPARSE) != const_cpu_to_le16(0); + else { + sparse = ntfs_rl_sparse(na->rl); + if (sparse == -1) { + errno = EIO; + goto error; + } + } + + /* Check whether attribute becomes sparse, unless check is delayed. */ + if ((holes != HOLES_DELAY) + && sparse + && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) { + /* + * Move attribute to another mft record, if attribute is too + * small to add compressed_size field to it and we have no + * free space in the current mft record. + */ + if ((le32_to_cpu(a->length) - + le16_to_cpu(a->mapping_pairs_offset) == 8) + && !(le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use))) { + + if (!NInoAttrList(na->ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(na->ni)) + goto leave; + goto retry; + } + if (ntfs_attr_record_move_away(ctx, 8)) { + ntfs_log_perror("Failed to move attribute"); + goto error; + } + ntfs_attr_put_search_ctx(ctx); + goto retry; + } + if (!(le32_to_cpu(a->length) - le16_to_cpu( + a->mapping_pairs_offset))) { + errno = EIO; + ntfs_log_perror("Mapping pairs space is 0"); + goto error; + } + + NAttrSetSparse(na); + a->flags |= ATTR_IS_SPARSE; + na->data_flags = a->flags; + a->compression_unit = STANDARD_COMPRESSION_UNIT; /* Windows + set it so, even if attribute is not actually compressed. */ + + memmove((u8*)a + le16_to_cpu(a->name_offset) + 8, + (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + + a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) + 8); + + a->mapping_pairs_offset = + cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) + 8); + } + + /* Attribute no longer sparse. */ + if (!sparse && (a->flags & ATTR_IS_SPARSE) && + !(a->flags & ATTR_IS_COMPRESSED)) { + + NAttrClearSparse(na); + a->flags &= ~ATTR_IS_SPARSE; + na->data_flags = a->flags; + a->compression_unit = 0; + + memmove((u8*)a + le16_to_cpu(a->name_offset) - 8, + (u8*)a + le16_to_cpu(a->name_offset), + a->name_length * sizeof(ntfschar)); + + if (le16_to_cpu(a->name_offset) >= 8) + a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) - 8); + + a->mapping_pairs_offset = + cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) - 8); + } + + /* Update compressed size if required. */ + if (NAttrFullyMapped(na) + && (sparse || (na->data_flags & ATTR_COMPRESSION_MASK))) { + s64 new_compr_size; + + new_compr_size = ntfs_rl_get_compressed_size(na->ni->vol, na->rl); + if (new_compr_size == -1) + goto error; + + na->compressed_size = new_compr_size; + a->compressed_size = cpu_to_sle64(new_compr_size); + } + /* + * Set FILE_NAME dirty flag, to update sparse bit and + * allocated size in the index. + */ + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) + na->ni->allocated_size = na->compressed_size; + else + na->ni->allocated_size = na->allocated_size; + NInoFileNameSetDirty(na->ni); + } +out: + return ret; +leave: ret = -1; goto out; /* return -1 */ +retry: ret = -2; goto out; +error: ret = -3; goto out; +} + +#define NTFS_VCN_DELETE_MARK -2 +/** + * ntfs_attr_update_mapping_pairs_i - see ntfs_attr_update_mapping_pairs + */ +static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn, + hole_type holes) +{ + ntfs_attr_search_ctx *ctx; + ntfs_inode *ni, *base_ni; + MFT_RECORD *m; + ATTR_RECORD *a; + VCN stop_vcn; + const runlist_element *stop_rl; + int err, mp_size, cur_max_mp_size, exp_max_mp_size, ret = -1; + BOOL finished_build; + BOOL first_updated = FALSE; + +retry: + if (!na || !na->rl) { + errno = EINVAL; + ntfs_log_perror("%s: na=%p", __FUNCTION__, na); + return -1; + } + + ntfs_log_trace("Entering for inode %llu, attr 0x%x\n", + (unsigned long long)na->ni->mft_no, na->type); + + if (!NAttrNonResident(na)) { + errno = EINVAL; + ntfs_log_perror("%s: resident attribute", __FUNCTION__); + return -1; + } + +#if PARTIAL_RUNLIST_UPDATING + /* + * For a file just been made sparse, we will have + * to reformat the first extent, so be sure the + * runlist is fully mapped and fully processed. + * Same if the file was sparse and is not any more. + * Note : not needed if the full runlist is to be processed + */ + if ((holes != HOLES_DELAY) + && (!NAttrFullyMapped(na) || from_vcn) + && !(na->data_flags & ATTR_IS_COMPRESSED)) { + BOOL changed; + + if (!(na->data_flags & ATTR_IS_SPARSE)) { + int sparse = 0; + runlist_element *xrl; + + /* + * If attribute was not sparse, we only + * have to check whether there is a hole + * in the updated region. + */ + for (xrl = na->rl; xrl->length; xrl++) { + if (xrl->lcn < 0) { + if (xrl->lcn == LCN_HOLE) { + sparse = 1; + break; + } + if (xrl->lcn != LCN_RL_NOT_MAPPED) { + sparse = -1; + break; + } + } + } + if (sparse < 0) { + ntfs_log_error("Could not check whether sparse\n"); + errno = EIO; + return (-1); + } + changed = sparse > 0; + } else { + /* + * If attribute was sparse, the compressed + * size has been maintained, and it gives + * and easy way to check whether the + * attribute is still sparse. + */ + changed = (((na->data_size - 1) + | (na->ni->vol->cluster_size - 1)) + 1) + == na->compressed_size; + } + if (changed) { + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_error("Could not map whole for sparse change\n"); + errno = EIO; + return (-1); + } + from_vcn = 0; + } + } +#endif + if (na->ni->nr_extents == -1) + base_ni = na->ni->base_ni; + else + base_ni = na->ni; + + ctx = ntfs_attr_get_search_ctx(base_ni, NULL); + if (!ctx) + return -1; + + /* Fill attribute records with new mapping pairs. */ + stop_vcn = 0; + stop_rl = na->rl; + finished_build = FALSE; + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) { + a = ctx->attr; + m = ctx->mrec; + if (!a->lowest_vcn) + first_updated = TRUE; + /* + * If runlist is updating not from the beginning, then set + * @stop_vcn properly, i.e. to the lowest vcn of record that + * contain @from_vcn. Also we do not need @from_vcn anymore, + * set it to 0 to make ntfs_attr_lookup enumerate attributes. + */ + if (from_vcn) { + LCN first_lcn; + + stop_vcn = sle64_to_cpu(a->lowest_vcn); + from_vcn = 0; + /* + * Check whether the first run we need to update is + * the last run in runlist, if so, then deallocate + * all attrubute extents starting this one. + */ + first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn); + if (first_lcn == LCN_EINVAL) { + errno = EIO; + ntfs_log_perror("Bad runlist"); + goto put_err_out; + } + if (first_lcn == LCN_ENOENT || + first_lcn == LCN_RL_NOT_MAPPED) + finished_build = TRUE; + } + + /* + * Check whether we finished mapping pairs build, if so mark + * extent as need to delete (by setting highest vcn to + * NTFS_VCN_DELETE_MARK (-2), we shall check it later and + * delete extent) and continue search. + */ + if (finished_build) { + ntfs_log_trace("Mark attr 0x%x for delete in inode " + "%lld.\n", (unsigned)le32_to_cpu(a->type), + (long long)ctx->ntfs_ino->mft_no); + a->highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + continue; + } + + switch (ntfs_attr_update_meta(a, na, m, holes, ctx)) { + case -1: return -1; + case -2: goto retry; + case -3: goto put_err_out; + } + + /* + * Determine maximum possible length of mapping pairs, + * if we shall *not* expand space for mapping pairs. + */ + cur_max_mp_size = le32_to_cpu(a->length) - + le16_to_cpu(a->mapping_pairs_offset); + /* + * Determine maximum possible length of mapping pairs in the + * current mft record, if we shall expand space for mapping + * pairs. + */ + exp_max_mp_size = le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) + cur_max_mp_size; + /* Get the size for the rest of mapping pairs array. */ + mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, stop_rl, + stop_vcn, exp_max_mp_size); + if (mp_size <= 0) { + ntfs_log_perror("%s: get MP size failed", __FUNCTION__); + goto put_err_out; + } + /* Test mapping pairs for fitting in the current mft record. */ + if (mp_size > exp_max_mp_size) { + /* + * Mapping pairs of $ATTRIBUTE_LIST attribute must fit + * in the base mft record. Try to move out other + * attributes and try again. + */ + if (na->type == AT_ATTRIBUTE_LIST) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_free_space(na->ni, mp_size - + cur_max_mp_size)) { + ntfs_log_perror("Attribute list is too " + "big. Defragment the " + "volume\n"); + return -1; + } + goto retry; + } + + /* Add attribute list if it isn't present, and retry. */ + if (!NInoAttrList(base_ni)) { + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_add_attrlist(base_ni)) { + ntfs_log_perror("Can not add attrlist"); + return -1; + } + goto retry; + } + + /* + * Set mapping pairs size to maximum possible for this + * mft record. We shall write the rest of mapping pairs + * to another MFT records. + */ + mp_size = exp_max_mp_size; + } + + /* Change space for mapping pairs if we need it. */ + if (((mp_size + 7) & ~7) != cur_max_mp_size) { + if (ntfs_attr_record_resize(m, a, + le16_to_cpu(a->mapping_pairs_offset) + + mp_size)) { + errno = EIO; + ntfs_log_perror("Failed to resize attribute"); + goto put_err_out; + } + } + + /* Update lowest vcn. */ + a->lowest_vcn = cpu_to_sle64(stop_vcn); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + if ((ctx->ntfs_ino->nr_extents == -1 || + NInoAttrList(ctx->ntfs_ino)) && + ctx->attr->type != AT_ATTRIBUTE_LIST) { + ctx->al_entry->lowest_vcn = cpu_to_sle64(stop_vcn); + ntfs_attrlist_mark_dirty(ctx->ntfs_ino); + } + + /* + * Generate the new mapping pairs array directly into the + * correct destination, i.e. the attribute record itself. + */ + if (!ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + le16_to_cpu( + a->mapping_pairs_offset), mp_size, na->rl, + stop_vcn, &stop_rl)) + finished_build = TRUE; + if (stop_rl) + stop_vcn = stop_rl->vcn; + else + stop_vcn = 0; + if (!finished_build && errno != ENOSPC) { + ntfs_log_perror("Failed to build mapping pairs"); + goto put_err_out; + } + a->highest_vcn = cpu_to_sle64(stop_vcn - 1); + } + /* Check whether error occurred. */ + if (errno != ENOENT) { + ntfs_log_perror("%s: Attribute lookup failed", __FUNCTION__); + goto put_err_out; + } + /* + * If the base extent was skipped in the above process, + * we still may have to update the sizes. + */ + if (!first_updated) { + le16 spcomp; + + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + a = ctx->attr; + a->allocated_size = cpu_to_sle64(na->allocated_size); + spcomp = na->data_flags + & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); + if (spcomp) + a->compressed_size = cpu_to_sle64(na->compressed_size); + if ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) { + na->ni->allocated_size + = (spcomp + ? na->compressed_size + : na->allocated_size); + NInoFileNameSetDirty(na->ni); + } + } else { + ntfs_log_error("Failed to update sizes in base extent\n"); + goto put_err_out; + } + } + + /* Deallocate not used attribute extents and return with success. */ + if (finished_build) { + ntfs_attr_reinit_search_ctx(ctx); + ntfs_log_trace("Deallocate marked extents.\n"); + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (sle64_to_cpu(ctx->attr->highest_vcn) != + NTFS_VCN_DELETE_MARK) + continue; + /* Remove unused attribute record. */ + if (ntfs_attr_record_rm(ctx)) { + ntfs_log_perror("Could not remove unused attr"); + goto put_err_out; + } + ntfs_attr_reinit_search_ctx(ctx); + } + if (errno != ENOENT) { + ntfs_log_perror("%s: Attr lookup failed", __FUNCTION__); + goto put_err_out; + } + ntfs_log_trace("Deallocate done.\n"); + ntfs_attr_put_search_ctx(ctx); + goto ok; + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + + /* Allocate new MFT records for the rest of mapping pairs. */ + while (1) { + /* Calculate size of rest mapping pairs. */ + mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, + na->rl, stop_vcn, INT_MAX); + if (mp_size <= 0) { + ntfs_log_perror("%s: get mp size failed", __FUNCTION__); + goto put_err_out; + } + /* Allocate new mft record, with special case for mft itself */ + if (!na->ni->mft_no) + ni = ntfs_mft_rec_alloc(na->ni->vol, + na->type == AT_DATA); + else + ni = ntfs_mft_record_alloc(na->ni->vol, base_ni); + if (!ni) { + ntfs_log_perror("Could not allocate new MFT record"); + goto put_err_out; + } + m = ni->mrec; + /* + * If mapping size exceed available space, set them to + * possible maximum. + */ + cur_max_mp_size = le32_to_cpu(m->bytes_allocated) - + le32_to_cpu(m->bytes_in_use) - + (offsetof(ATTR_RECORD, compressed_size) + + (((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) ? + sizeof(a->compressed_size) : 0)) - + ((sizeof(ntfschar) * na->name_len + 7) & ~7); + if (mp_size > cur_max_mp_size) + mp_size = cur_max_mp_size; + /* Add attribute extent to new record. */ + err = ntfs_non_resident_attr_record_add(ni, na->type, + na->name, na->name_len, stop_vcn, mp_size, + na->data_flags); + if (err == -1) { + err = errno; + ntfs_log_perror("Could not add attribute extent"); + if (ntfs_mft_record_free(na->ni->vol, ni)) + ntfs_log_perror("Could not free MFT record"); + errno = err; + goto put_err_out; + } + a = (ATTR_RECORD*)((u8*)m + err); + + err = ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), mp_size, na->rl, + stop_vcn, &stop_rl); + if (stop_rl) + stop_vcn = stop_rl->vcn; + else + stop_vcn = 0; + if (err < 0 && errno != ENOSPC) { + err = errno; + ntfs_log_perror("Failed to build MP"); + if (ntfs_mft_record_free(na->ni->vol, ni)) + ntfs_log_perror("Couldn't free MFT record"); + errno = err; + goto put_err_out; + } + a->highest_vcn = cpu_to_sle64(stop_vcn - 1); + ntfs_inode_mark_dirty(ni); + /* All mapping pairs has been written. */ + if (!err) + break; + } +ok: + NAttrClearRunlistDirty(na); + ret = 0; +out: + return ret; +put_err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + goto out; +} +#undef NTFS_VCN_DELETE_MARK + +/** + * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute + * @na: non-resident ntfs open attribute for which we need update + * @from_vcn: update runlist starting this VCN + * + * Build mapping pairs from @na->rl and write them to the disk. Also, this + * function updates sparse bit, allocated and compressed size (allocates/frees + * space for this field if required). + * + * @na->allocated_size should be set to correct value for the new runlist before + * call to this function. Vice-versa @na->compressed_size will be calculated and + * set to correct value during this function. + * + * FIXME: This function does not update sparse bit and compressed size correctly + * if called with @from_vcn != 0. + * + * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * EINVAL - Invalid arguments passed. + * ENOMEM - Not enough memory to complete operation. + * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST + * or there is no free MFT records left to allocate. + */ +int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn, HOLES_OK); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute + * @na: non-resident ntfs attribute to shrink + * @newsize: new size (in bytes) to which to shrink the attribute + * + * Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * ENOMEM - Not enough memory to complete operation. + * ERANGE - @newsize is not valid for the attribute type of @na. + */ +static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) +{ + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx; + VCN first_free_vcn; + s64 nr_freed_clusters; + int err; + + ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", (unsigned long long) + na->ni->mft_no, na->type, (long long)newsize); + + vol = na->ni->vol; + + /* + * Check the attribute type and the corresponding minimum size + * against @newsize and fail if @newsize is too small. + */ + if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { + if (errno == ERANGE) { + ntfs_log_trace("Eeek! Size bounds check failed. " + "Aborting...\n"); + } else if (errno == ENOENT) + errno = EIO; + return -1; + } + + /* The first cluster outside the new allocation. */ + if (na->data_flags & ATTR_COMPRESSION_MASK) + /* + * For compressed files we must keep full compressions blocks, + * but currently we do not decompress/recompress the last + * block to truncate the data, so we may leave more allocated + * clusters than really needed. + */ + first_free_vcn = (((newsize - 1) + | (na->compression_block_size - 1)) + 1) + >> vol->cluster_size_bits; + else + first_free_vcn = (newsize + vol->cluster_size - 1) >> + vol->cluster_size_bits; + /* + * Compare the new allocation with the old one and only deallocate + * clusters if there is a change. + */ + if ((na->allocated_size >> vol->cluster_size_bits) != first_free_vcn) { + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist " + "failed.\n"); + return -1; + } + /* Deallocate all clusters starting with the first free one. */ + nr_freed_clusters = ntfs_cluster_free(vol, na, first_free_vcn, + -1); + if (nr_freed_clusters < 0) { + ntfs_log_trace("Eeek! Freeing of clusters failed. " + "Aborting...\n"); + return -1; + } + + /* Truncate the runlist itself. */ + if (ntfs_rl_truncate(&na->rl, first_free_vcn)) { + /* + * Failed to truncate the runlist, so just throw it + * away, it will be mapped afresh on next use. + */ + free(na->rl); + na->rl = NULL; + ntfs_log_trace("Eeek! Run list truncation failed.\n"); + return -1; + } + NAttrSetRunlistDirty(na); + + /* Prepare to mapping pairs update. */ + na->allocated_size = first_free_vcn << vol->cluster_size_bits; + /* Write mapping pairs for new runlist. */ + if (ntfs_attr_update_mapping_pairs(na, 0 /*first_free_vcn*/)) { + ntfs_log_trace("Eeek! Mapping pairs update failed. " + "Leaving inconstant metadata. " + "Run chkdsk.\n"); + return -1; + } + } + + /* Get the first attribute record. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + + if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + err = errno; + if (err == ENOENT) + err = EIO; + ntfs_log_trace("Eeek! Lookup of first attribute extent failed. " + "Leaving inconstant metadata.\n"); + goto put_err_out; + } + + /* Update data and initialized size. */ + na->data_size = newsize; + ctx->attr->data_size = cpu_to_sle64(newsize); + if (newsize < na->initialized_size) { + na->initialized_size = newsize; + ctx->attr->initialized_size = cpu_to_sle64(newsize); + } + /* Update data size in the index. */ + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } + } + + /* If the attribute now has zero size, make it resident. */ + if (!newsize) { + if (!(na->data_flags & ATTR_IS_ENCRYPTED) + && ntfs_attr_make_resident(na, ctx)) { + /* If couldn't make resident, just continue. */ + if (errno != EPERM) + ntfs_log_error("Failed to make attribute " + "resident. Leaving as is...\n"); + } + } + + /* Set the inode dirty so it is written out later. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + /* Done! */ + ntfs_attr_put_search_ctx(ctx); + return 0; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_non_resident_attr_expand - expand a non-resident, open ntfs attribute + * @na: non-resident ntfs attribute to expand + * @newsize: new size (in bytes) to which to expand the attribute + * + * Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes, + * by allocating new clusters. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * ENOMEM - Not enough memory to complete operation. + * ERANGE - @newsize is not valid for the attribute type of @na. + * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. + */ +static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize, + hole_type holes) +{ + LCN lcn_seek_from; + VCN first_free_vcn; + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx; + runlist *rl, *rln; + s64 org_alloc_size; + int err; + + ntfs_log_trace("Inode %lld, attr 0x%x, new size %lld old size %lld\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)newsize, (long long)na->data_size); + + vol = na->ni->vol; + + /* + * Check the attribute type and the corresponding maximum size + * against @newsize and fail if @newsize is too big. + */ + if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { + if (errno == ENOENT) + errno = EIO; + ntfs_log_perror("%s: bounds check failed", __FUNCTION__); + return -1; + } + + if (na->type == AT_DATA) + NAttrSetDataAppending(na); + /* Save for future use. */ + org_alloc_size = na->allocated_size; + /* The first cluster outside the new allocation. */ + first_free_vcn = (newsize + vol->cluster_size - 1) >> + vol->cluster_size_bits; + /* + * Compare the new allocation with the old one and only allocate + * clusters if there is a change. + */ + if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) { +#if PARTIAL_RUNLIST_UPDATING + s64 start_update; + + /* + * Update from the last previously allocated run, + * as we may have to expand an existing hole. + */ + start_update = na->allocated_size >> vol->cluster_size_bits; + if (start_update) + start_update--; + if (ntfs_attr_map_partial_runlist(na, start_update)) { + ntfs_log_perror("failed to map partial runlist"); + return -1; + } +#else + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_perror("ntfs_attr_map_whole_runlist failed"); + return -1; + } +#endif + + /* + * If we extend $DATA attribute on NTFS 3+ volume, we can add + * sparse runs instead of real allocation of clusters. + */ + if ((na->type == AT_DATA) && (vol->major_ver >= 3) + && (holes != HOLES_NO)) { + rl = ntfs_malloc(0x1000); + if (!rl) + return -1; + + rl[0].vcn = (na->allocated_size >> + vol->cluster_size_bits); + rl[0].lcn = LCN_HOLE; + rl[0].length = first_free_vcn - + (na->allocated_size >> vol->cluster_size_bits); + rl[1].vcn = first_free_vcn; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + } else { + /* + * Determine first after last LCN of attribute. + * We will start seek clusters from this LCN to avoid + * fragmentation. If there are no valid LCNs in the + * attribute let the cluster allocator choose the + * starting LCN. + */ + lcn_seek_from = -1; + if (na->rl->length) { + /* Seek to the last run list element. */ + for (rl = na->rl; (rl + 1)->length; rl++) + ; + /* + * If the last LCN is a hole or similar seek + * back to last valid LCN. + */ + while (rl->lcn < 0 && rl != na->rl) + rl--; + /* + * Only set lcn_seek_from it the LCN is valid. + */ + if (rl->lcn >= 0) + lcn_seek_from = rl->lcn + rl->length; + } + + rl = ntfs_cluster_alloc(vol, na->allocated_size >> + vol->cluster_size_bits, first_free_vcn - + (na->allocated_size >> + vol->cluster_size_bits), lcn_seek_from, + DATA_ZONE); + if (!rl) { + ntfs_log_perror("Cluster allocation failed " + "(%lld)", + (long long)first_free_vcn - + ((long long)na->allocated_size >> + vol->cluster_size_bits)); + return -1; + } + } + + /* Append new clusters to attribute runlist. */ + rln = ntfs_runlists_merge(na->rl, rl); + if (!rln) { + /* Failed, free just allocated clusters. */ + err = errno; + ntfs_log_perror("Run list merge failed"); + ntfs_cluster_free_from_rl(vol, rl); + free(rl); + errno = err; + return -1; + } + na->rl = rln; + NAttrSetRunlistDirty(na); + + /* Prepare to mapping pairs update. */ + na->allocated_size = first_free_vcn << vol->cluster_size_bits; +#if PARTIAL_RUNLIST_UPDATING + /* + * Write mapping pairs for new runlist, unless this is + * a temporary state before appending data. + * If the update is not done, we must be sure to do + * it later, and to get to a clean state even on errors. + */ + if ((holes != HOLES_DELAY) + && ntfs_attr_update_mapping_pairs_i(na, start_update, + holes)) { +#else + /* Write mapping pairs for new runlist. */ + if (ntfs_attr_update_mapping_pairs(na, 0)) { +#endif + err = errno; + ntfs_log_perror("Mapping pairs update failed"); + goto rollback; + } + } + + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) { + err = errno; + if (na->allocated_size == org_alloc_size) { + errno = err; + return -1; + } else + goto rollback; + } + + if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + err = errno; + ntfs_log_perror("Lookup of first attribute extent failed"); + if (err == ENOENT) + err = EIO; + if (na->allocated_size != org_alloc_size) { + ntfs_attr_put_search_ctx(ctx); + goto rollback; + } else + goto put_err_out; + } + + /* Update data size. */ + na->data_size = newsize; + ctx->attr->data_size = cpu_to_sle64(newsize); + /* Update data size in the index. */ + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } + } + /* Set the inode dirty so it is written out later. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + /* Done! */ + ntfs_attr_put_search_ctx(ctx); + return 0; +rollback: + /* Free allocated clusters. */ + if (ntfs_cluster_free(vol, na, org_alloc_size >> + vol->cluster_size_bits, -1) < 0) { + err = EIO; + ntfs_log_perror("Leaking clusters"); + } + /* Now, truncate the runlist itself. */ + if (ntfs_rl_truncate(&na->rl, org_alloc_size >> + vol->cluster_size_bits)) { + /* + * Failed to truncate the runlist, so just throw it away, it + * will be mapped afresh on next use. + */ + free(na->rl); + na->rl = NULL; + ntfs_log_perror("Couldn't truncate runlist. Rollback failed"); + } else { + NAttrSetRunlistDirty(na); + /* Prepare to mapping pairs update. */ + na->allocated_size = org_alloc_size; + /* Restore mapping pairs. */ + if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> + vol->cluster_size_bits*/)) { + ntfs_log_perror("Failed to restore old mapping pairs"); + } + } + errno = err; + return -1; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + + +static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize, + hole_type holes) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_non_resident_attr_expand_i(na, newsize, holes); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_attr_truncate - resize an ntfs attribute + * @na: open ntfs attribute to resize + * @newsize: new size (in bytes) to which to resize the attribute + * @holes: how to create a hole if expanding + * + * Change the size of an open ntfs attribute @na to @newsize bytes. If the + * attribute is made bigger and the attribute is resident the newly + * "allocated" space is cleared and if the attribute is non-resident the + * newly allocated space is marked as not initialised and no real allocation + * on disk is performed. + * + * On success return 0. + * On error return values are: + * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT + * STATUS_ERROR - otherwise + * The following error codes are defined: + * EINVAL - Invalid arguments were passed to the function. + * EOPNOTSUPP - The desired resize is not implemented yet. + * EACCES - Encrypted attribute. + */ +static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, + hole_type holes) +{ + int ret = STATUS_ERROR; + s64 fullsize; + BOOL compressed; + + if (!na || newsize < 0 || + (na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) { + ntfs_log_trace("Invalid arguments passed.\n"); + errno = EINVAL; + return STATUS_ERROR; + } + + ntfs_log_enter("Entering for inode %lld, attr 0x%x, size %lld\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)newsize); + + if (na->data_size == newsize) { + ntfs_log_trace("Size is already ok\n"); + ret = STATUS_OK; + goto out; + } + /* + * Encrypted attributes are not supported. We return access denied, + * which is what Windows NT4 does, too. + */ + if ((na->data_flags & ATTR_IS_ENCRYPTED) && !na->ni->vol->efs_raw) { + errno = EACCES; + ntfs_log_trace("Cannot truncate encrypted attribute\n"); + goto out; + } + /* + * TODO: Implement making handling of compressed attributes. + * Currently we can only expand the attribute or delete it, + * and only for ATTR_IS_COMPRESSED. This is however possible + * for resident attributes when there is no open fuse context + * (important case : $INDEX_ROOT:$I30) + */ + compressed = (na->data_flags & ATTR_COMPRESSION_MASK) + != const_cpu_to_le16(0); + if (compressed + && NAttrNonResident(na) + && ((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED)) { + errno = EOPNOTSUPP; + ntfs_log_perror("Failed to truncate compressed attribute"); + goto out; + } + if (NAttrNonResident(na)) { + /* + * For compressed data, the last block must be fully + * allocated, and we do not know the size of compression + * block until the attribute has been made non-resident. + * Moreover we can only process a single compression + * block at a time (from where we are about to write), + * so we silently do not allocate more. + * + * Note : do not request upsizing of compressed files + * unless being able to face the consequences ! + */ + if (compressed && newsize && (newsize > na->data_size)) + fullsize = (na->initialized_size + | (na->compression_block_size - 1)) + 1; + else + fullsize = newsize; + if (fullsize > na->data_size) + ret = ntfs_non_resident_attr_expand(na, fullsize, + holes); + else + ret = ntfs_non_resident_attr_shrink(na, fullsize); + } else + ret = ntfs_resident_attr_resize_i(na, newsize, holes); +out: + ntfs_log_leave("Return status %d\n", ret); + return ret; +} + +/* + * Resize an attribute, creating a hole if relevant + */ + +int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) +{ + int r; + + r = ntfs_attr_truncate_i(na, newsize, HOLES_OK); + NAttrClearDataAppending(na); + NAttrClearBeingNonResident(na); + return (r); +} + +/* + * Resize an attribute, avoiding hole creation + */ + +int ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize) +{ + return (ntfs_attr_truncate_i(na, newsize, HOLES_NO)); +} + +/* + * Stuff a hole in a compressed file + * + * An unallocated hole must be aligned on compression block size. + * If needed current block and target block are stuffed with zeroes. + * + * Returns 0 if succeeded, + * -1 if it failed (as explained in errno) + */ + +static int stuff_hole(ntfs_attr *na, const s64 pos) +{ + s64 size; + s64 begin_size; + s64 end_size; + char *buf; + int ret; + + ret = 0; + /* + * If the attribute is resident, the compression block size + * is not defined yet and we can make no decision. + * So we first try resizing to the target and if the + * attribute is still resident, we're done + */ + if (!NAttrNonResident(na)) { + ret = ntfs_resident_attr_resize(na, pos); + if (!ret && !NAttrNonResident(na)) + na->initialized_size = na->data_size = pos; + } + if (!ret && NAttrNonResident(na)) { + /* does the hole span over several compression block ? */ + if ((pos ^ na->initialized_size) + & ~(na->compression_block_size - 1)) { + begin_size = ((na->initialized_size - 1) + | (na->compression_block_size - 1)) + + 1 - na->initialized_size; + end_size = pos & (na->compression_block_size - 1); + size = (begin_size > end_size ? begin_size : end_size); + } else { + /* short stuffing in a single compression block */ + begin_size = size = pos - na->initialized_size; + end_size = 0; + } + if (size) + buf = (char*)ntfs_malloc(size); + else + buf = (char*)NULL; + if (buf || !size) { + memset(buf,0,size); + /* stuff into current block */ + if (begin_size + && (ntfs_attr_pwrite(na, + na->initialized_size, begin_size, buf) + != begin_size)) + ret = -1; + /* create an unstuffed hole */ + if (!ret + && ((na->initialized_size + end_size) < pos) + && ntfs_non_resident_attr_expand(na, + pos - end_size, HOLES_OK)) + ret = -1; + else + na->initialized_size + = na->data_size = pos - end_size; + /* stuff into the target block */ + if (!ret && end_size + && (ntfs_attr_pwrite(na, + na->initialized_size, end_size, buf) + != end_size)) + ret = -1; + if (buf) + free(buf); + } else + ret = -1; + } + /* make absolutely sure we have reached the target */ + if (!ret && (na->initialized_size != pos)) { + ntfs_log_error("Failed to stuff a compressed file" + "target %lld reached %lld\n", + (long long)pos, (long long)na->initialized_size); + errno = EIO; + ret = -1; + } + return (ret); +} + +/** + * ntfs_attr_readall - read the entire data from an ntfs attribute + * @ni: open ntfs inode in which the ntfs attribute resides + * @type: attribute type + * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL + * @name_len: length of attribute @name in Unicode characters (if @name given) + * @data_size: if non-NULL then store here the data size + * + * This function will read the entire content of an ntfs attribute. + * If @name is AT_UNNAMED then look specifically for an unnamed attribute. + * If @name is NULL then the attribute could be either named or not. + * In both those cases @name_len is not used at all. + * + * On success a buffer is allocated with the content of the attribute + * and which needs to be freed when it's not needed anymore. If the + * @data_size parameter is non-NULL then the data size is set there. + * + * On error NULL is returned with errno set to the error code. + */ +void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, + ntfschar *name, u32 name_len, s64 *data_size) +{ + ntfs_attr *na; + void *data, *ret = NULL; + s64 size; + + ntfs_log_enter("Entering\n"); + + na = ntfs_attr_open(ni, type, name, name_len); + if (!na) { + ntfs_log_perror("ntfs_attr_open failed, inode %lld attr 0x%lx", + (long long)ni->mft_no,(long)le32_to_cpu(type)); + goto err_exit; + } + data = ntfs_malloc(na->data_size); + if (!data) + goto out; + + size = ntfs_attr_pread(na, 0, na->data_size, data); + if (size != na->data_size) { + ntfs_log_perror("ntfs_attr_pread failed"); + free(data); + goto out; + } + ret = data; + if (data_size) + *data_size = size; +out: + ntfs_attr_close(na); +err_exit: + ntfs_log_leave("\n"); + return ret; +} + +/* + * Read some data from a data attribute + * + * Returns the amount of data read, negative if there was an error + */ + +int ntfs_attr_data_read(ntfs_inode *ni, + ntfschar *stream_name, int stream_name_len, + char *buf, size_t size, off_t offset) +{ + ntfs_attr *na = NULL; + int res, total = 0; + + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + if ((size_t)offset < (size_t)na->data_size) { + if (offset + size > (size_t)na->data_size) + size = na->data_size - offset; + while (size) { + res = ntfs_attr_pread(na, offset, size, buf + total); + if ((off_t)res < (off_t)size) + ntfs_log_perror("ntfs_attr_pread partial read " + "(%lld : %lld <> %d)", + (long long)offset, + (long long)size, res); + if (res <= 0) { + res = -errno; + goto exit; + } + size -= res; + offset += res; + total += res; + } + } + res = total; +exit: + if (na) + ntfs_attr_close(na); + return res; +} + + +/* + * Write some data into a data attribute + * + * Returns the amount of data written, negative if there was an error + */ + +int ntfs_attr_data_write(ntfs_inode *ni, ntfschar *stream_name, + int stream_name_len, const char *buf, size_t size, off_t offset) +{ + ntfs_attr *na = NULL; + int res, total = 0; + + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + while (size) { + res = ntfs_attr_pwrite(na, offset, size, buf + total); + if (res < (s64)size) + ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: " + "%lld <> %d)", (long long)offset, + (long long)size, res); + if (res <= 0) { + res = -errno; + goto exit; + } + size -= res; + offset += res; + total += res; + } + res = total; +exit: + if (na) + ntfs_attr_close(na); + return res; +} + +/* + * Shrink the size of a data attribute if needed + * + * For non-resident attributes only. + * The space remains allocated. + * + * Returns 0 if successful + * -1 if failed, with errno telling why + */ + + +int ntfs_attr_shrink_size(ntfs_inode *ni, ntfschar *stream_name, + int stream_name_len, off_t offset) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + int res; + + res = -1; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (!ntfs_attr_lookup(AT_DATA, stream_name, stream_name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + a = ctx->attr; + + if (a->non_resident + && (sle64_to_cpu(a->initialized_size) > offset)) { + a->initialized_size = cpu_to_le64(offset); + a->data_size = a->initialized_size; + } + res = 0; + } + ntfs_attr_put_search_ctx(ctx); + } + return (res); +} + +int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, const ntfschar *name, + u32 name_len) +{ + ntfs_attr_search_ctx *ctx; + int ret; + + ntfs_log_trace("Entering\n"); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return 0; + + ret = ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 0, NULL, 0, + ctx); + + ntfs_attr_put_search_ctx(ctx); + + return !ret; +} + +int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, + u32 name_len) +{ + ntfs_attr *na; + int ret; + + ntfs_log_trace("Entering\n"); + + if (!ni) { + ntfs_log_error("%s: NULL inode pointer", __FUNCTION__); + errno = EINVAL; + return -1; + } + + na = ntfs_attr_open(ni, type, name, name_len); + if (!na) { + /* do not log removal of non-existent stream */ + if (type != AT_DATA) { + ntfs_log_perror("Failed to open attribute 0x%02x of inode " + "0x%llx", type, (unsigned long long)ni->mft_no); + } + return -1; + } + + ret = ntfs_attr_rm(na); + if (ret) + ntfs_log_perror("Failed to remove attribute 0x%02x of inode " + "0x%llx", type, (unsigned long long)ni->mft_no); + ntfs_attr_close(na); + + return ret; +} + +/* Below macros are 32-bit ready. */ +#define BCX(x) ((x) - (((x) >> 1) & 0x77777777) - \ + (((x) >> 2) & 0x33333333) - \ + (((x) >> 3) & 0x11111111)) +#define BITCOUNT(x) (((BCX(x) + (BCX(x) >> 4)) & 0x0F0F0F0F) % 255) + +static u8 *ntfs_init_lut256(void) +{ + int i; + u8 *lut; + + lut = ntfs_malloc(256); + if (lut) + for(i = 0; i < 256; i++) + *(lut + i) = 8 - BITCOUNT(i); + return lut; +} + +s64 ntfs_attr_get_free_bits(ntfs_attr *na) +{ + u8 *buf, *lut; + s64 br = 0; + s64 total = 0; + s64 nr_free = 0; + + lut = ntfs_init_lut256(); + if (!lut) + return -1; + + buf = ntfs_malloc(65536); + if (!buf) + goto out; + + while (1) { + u32 *p; + br = ntfs_attr_pread(na, total, 65536, buf); + if (br <= 0) + break; + total += br; + p = (u32 *)buf + br / 4 - 1; + for (; (u8 *)p >= buf; p--) { + nr_free += lut[ *p & 255] + + lut[(*p >> 8) & 255] + + lut[(*p >> 16) & 255] + + lut[(*p >> 24) ]; + } + switch (br % 4) { + case 3: nr_free += lut[*(buf + br - 3)]; + case 2: nr_free += lut[*(buf + br - 2)]; + case 1: nr_free += lut[*(buf + br - 1)]; + } + } + free(buf); +out: + free(lut); + if (!total || br < 0) + return -1; + return nr_free; +} diff --git a/libntfs-3g/attrlist.c b/libntfs-3g/attrlist.c new file mode 100755 index 0000000000000000000000000000000000000000..9c62f316146fb4e50b82879a0d5ca110e4dc0e03 --- /dev/null +++ b/libntfs-3g/attrlist.c @@ -0,0 +1,314 @@ +/** + * attrlist.c - Attribute list attribute handling code. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2006 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "types.h" +#include "layout.h" +#include "attrib.h" +#include "attrlist.h" +#include "debug.h" +#include "unistr.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_attrlist_need - check whether inode need attribute list + * @ni: opened ntfs inode for which perform check + * + * Check whether all are attributes belong to one MFT record, in that case + * attribute list is not needed. + * + * Return 1 if inode need attribute list, 0 if not, -1 on error with errno set + * to the error code. If function succeed errno set to 0. The following error + * codes are defined: + * EINVAL - Invalid arguments passed to function or attribute haven't got + * attribute list. + */ +int ntfs_attrlist_need(ntfs_inode *ni) +{ + ATTR_LIST_ENTRY *ale; + + if (!ni) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + + if (!NInoAttrList(ni)) { + ntfs_log_trace("Inode haven't got attribute list.\n"); + errno = EINVAL; + return -1; + } + + if (!ni->attr_list) { + ntfs_log_trace("Corrupt in-memory struct.\n"); + errno = EINVAL; + return -1; + } + + errno = 0; + ale = (ATTR_LIST_ENTRY *)ni->attr_list; + while ((u8*)ale < ni->attr_list + ni->attr_list_size) { + if (MREF_LE(ale->mft_reference) != ni->mft_no) + return 1; + ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); + } + return 0; +} + +/** + * ntfs_attrlist_entry_add - add an attribute list attribute entry + * @ni: opened ntfs inode, which contains that attribute + * @attr: attribute record to add to attribute list + * + * Return 0 on success and -1 on error with errno set to the error code. The + * following error codes are defined: + * EINVAL - Invalid arguments passed to function. + * ENOMEM - Not enough memory to allocate necessary buffers. + * EIO - I/O error occurred or damaged filesystem. + * EEXIST - Such attribute already present in attribute list. + */ +int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) +{ + ATTR_LIST_ENTRY *ale; + MFT_REF mref; + ntfs_attr *na = NULL; + ntfs_attr_search_ctx *ctx; + u8 *new_al; + int entry_len, entry_offset, err; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + (long long) ni->mft_no, + (unsigned) le32_to_cpu(attr->type)); + + if (!ni || !attr) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); + + if (ni->nr_extents == -1) + ni = ni->base_ni; + + if (!NInoAttrList(ni)) { + ntfs_log_trace("Attribute list isn't present.\n"); + errno = ENOENT; + return -1; + } + + /* Determine size and allocate memory for new attribute list. */ + entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * + attr->name_length + 7) & ~7; + new_al = ntfs_calloc(ni->attr_list_size + entry_len); + if (!new_al) + return -1; + + /* Find place for the new entry. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) + ((u8*)attr + le16_to_cpu(attr->name_offset)) : + AT_UNNAMED, attr->name_length, CASE_SENSITIVE, + (attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) : + 0, (attr->non_resident) ? NULL : ((u8*)attr + + le16_to_cpu(attr->value_offset)), (attr->non_resident) ? + 0 : le32_to_cpu(attr->value_length), ctx)) { + /* Found some extent, check it to be before new extent. */ + if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) { + err = EEXIST; + ntfs_log_trace("Such attribute already present in the " + "attribute list.\n"); + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + /* Add new entry after this extent. */ + ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + + le16_to_cpu(ctx->al_entry->length)); + } else { + /* Check for real errors. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_trace("Attribute lookup failed.\n"); + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + /* No previous extents found. */ + ale = ctx->al_entry; + } + /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ + ntfs_attr_put_search_ctx(ctx); + + /* Determine new entry offset. */ + entry_offset = ((u8 *)ale - ni->attr_list); + /* Set pointer to new entry. */ + ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset); + /* Zero it to fix valgrind warning. */ + memset(ale, 0, entry_len); + /* Form new entry. */ + ale->type = attr->type; + ale->length = cpu_to_le16(entry_len); + ale->name_length = attr->name_length; + ale->name_offset = offsetof(ATTR_LIST_ENTRY, name); + if (attr->non_resident) + ale->lowest_vcn = attr->lowest_vcn; + else + ale->lowest_vcn = 0; + ale->mft_reference = mref; + ale->instance = attr->instance; + memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), + attr->name_length * sizeof(ntfschar)); + + /* Resize $ATTRIBUTE_LIST to new length. */ + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); + goto err_out; + } + if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) { + err = errno; + ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); + goto err_out; + } + + /* Copy entries from old attribute list to new. */ + memcpy(new_al, ni->attr_list, entry_offset); + memcpy(new_al + entry_offset + entry_len, ni->attr_list + + entry_offset, ni->attr_list_size - entry_offset); + + /* Set new runlist. */ + free(ni->attr_list); + ni->attr_list = new_al; + ni->attr_list_size = ni->attr_list_size + entry_len; + NInoAttrListSetDirty(ni); + /* Done! */ + ntfs_attr_close(na); + return 0; +err_out: + if (na) + ntfs_attr_close(na); + free(new_al); + errno = err; + return -1; +} + +/** + * ntfs_attrlist_entry_rm - remove an attribute list attribute entry + * @ctx: attribute search context describing the attribute list entry + * + * Remove the attribute list entry @ctx->al_entry from the attribute list. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) +{ + u8 *new_al; + int new_al_len; + ntfs_inode *base_ni; + ntfs_attr *na; + ATTR_LIST_ENTRY *ale; + int err; + + if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + ale = ctx->al_entry; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n", + (long long) ctx->ntfs_ino->mft_no, + (unsigned) le32_to_cpu(ctx->al_entry->type), + (long long) le64_to_cpu(ctx->al_entry->lowest_vcn)); + + if (!NInoAttrList(base_ni)) { + ntfs_log_trace("Attribute list isn't present.\n"); + errno = ENOENT; + return -1; + } + + /* Allocate memory for new attribute list. */ + new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); + new_al = ntfs_calloc(new_al_len); + if (!new_al) + return -1; + + /* Reisze $ATTRIBUTE_LIST to new length. */ + na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); + goto err_out; + } + if (ntfs_attr_truncate(na, new_al_len)) { + err = errno; + ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); + goto err_out; + } + + /* Copy entries from old attribute list to new. */ + memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list); + memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu( + ale->length), new_al_len - ((u8*)ale - base_ni->attr_list)); + + /* Set new runlist. */ + free(base_ni->attr_list); + base_ni->attr_list = new_al; + base_ni->attr_list_size = new_al_len; + NInoAttrListSetDirty(base_ni); + /* Done! */ + ntfs_attr_close(na); + return 0; +err_out: + if (na) + ntfs_attr_close(na); + free(new_al); + errno = err; + return -1; +} diff --git a/libntfs-3g/bitmap.c b/libntfs-3g/bitmap.c new file mode 100755 index 0000000000000000000000000000000000000000..65162a2946003ee63805d906625023eecd53052a --- /dev/null +++ b/libntfs-3g/bitmap.c @@ -0,0 +1,300 @@ +/** + * bitmap.c - Bitmap handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2006 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "types.h" +#include "attrib.h" +#include "bitmap.h" +#include "debug.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_bit_set - set a bit in a field of bits + * @bitmap: field of bits + * @bit: bit to set + * @new_value: value to set bit to (0 or 1) + * + * Set the bit @bit in the @bitmap to @new_value. Ignore all errors. + */ +void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value) +{ + if (!bitmap || new_value > 1) + return; + if (!new_value) + bitmap[bit >> 3] &= ~(1 << (bit & 7)); + else + bitmap[bit >> 3] |= (1 << (bit & 7)); +} + +/** + * ntfs_bit_get - get value of a bit in a field of bits + * @bitmap: field of bits + * @bit: bit to get + * + * Get and return the value of the bit @bit in @bitmap (0 or 1). + * Return -1 on error. + */ +char ntfs_bit_get(const u8 *bitmap, const u64 bit) +{ + if (!bitmap) + return -1; + return (bitmap[bit >> 3] >> (bit & 7)) & 1; +} + +/** + * ntfs_bit_get_and_set - get value of a bit in a field of bits and set it + * @bitmap: field of bits + * @bit: bit to get/set + * @new_value: value to set bit to (0 or 1) + * + * Return the value of the bit @bit and set it to @new_value (0 or 1). + * Return -1 on error. + */ +char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value) +{ + register u8 old_bit, shift; + + if (!bitmap || new_value > 1) + return -1; + shift = bit & 7; + old_bit = (bitmap[bit >> 3] >> shift) & 1; + if (new_value != old_bit) + bitmap[bit >> 3] ^= 1 << shift; + return old_bit; +} + +/** + * ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value + * @na: attribute containing the bitmap + * @start_bit: first bit to set + * @count: number of bits to set + * @value: value to set the bits to (i.e. 0 or 1) + * + * Set @count bits starting at bit @start_bit in the bitmap described by the + * attribute @na to @value, where @value is either 0 or 1. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit, + s64 count, int value) +{ + s64 bufsize, br; + u8 *buf, *lastbyte_buf; + int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1; + + if (!na || start_bit < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: Invalid argument (%p, %lld, %lld)", + __FUNCTION__, na, (long long)start_bit, (long long)count); + return -1; + } + + bit = start_bit & 7; + if (bit) + firstbyte = 1; + else + firstbyte = 0; + + /* Calculate the required buffer size in bytes, capping it at 8kiB. */ + bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte; + if (bufsize > 8192) + bufsize = 8192; + + buf = ntfs_malloc(bufsize); + if (!buf) + return -1; + + /* Depending on @value, zero or set all bits in the allocated buffer. */ + memset(buf, value ? 0xff : 0, bufsize); + + /* If there is a first partial byte... */ + if (bit) { + /* read it in... */ + br = ntfs_attr_pread(na, start_bit >> 3, 1, buf); + if (br != 1) { + if (br >= 0) + errno = EIO; + goto free_err_out; + } + /* and set or clear the appropriate bits in it. */ + while ((bit & 7) && count--) { + if (value) + *buf |= 1 << bit++; + else + *buf &= ~(1 << bit++); + } + /* Update @start_bit to the new position. */ + start_bit = (start_bit + 7) & ~7; + } + + /* Loop until @count reaches zero. */ + lastbyte = 0; + lastbyte_buf = NULL; + bit = count & 7; + do { + /* If there is a last partial byte... */ + if (count > 0 && bit) { + lastbyte_pos = ((count + 7) >> 3) + firstbyte; + if (!lastbyte_pos) { + // FIXME: Eeek! BUG! + ntfs_log_error("Lastbyte is zero. Leaving " + "inconsistent metadata.\n"); + errno = EIO; + goto free_err_out; + } + /* and it is in the currently loaded bitmap window... */ + if (lastbyte_pos <= bufsize) { + lastbyte_buf = buf + lastbyte_pos - 1; + + /* read the byte in... */ + br = ntfs_attr_pread(na, (start_bit + count) >> + 3, 1, lastbyte_buf); + if (br != 1) { + // FIXME: Eeek! We need rollback! (AIA) + if (br >= 0) + errno = EIO; + ntfs_log_perror("Reading of last byte " + "failed (%lld). Leaving inconsistent " + "metadata", (long long)br); + goto free_err_out; + } + /* and set/clear the appropriate bits in it. */ + while (bit && count--) { + if (value) + *lastbyte_buf |= 1 << --bit; + else + *lastbyte_buf &= ~(1 << --bit); + } + /* We don't want to come back here... */ + bit = 0; + /* We have a last byte that we have handled. */ + lastbyte = 1; + } + } + + /* Write the prepared buffer to disk. */ + tmp = (start_bit >> 3) - firstbyte; + br = ntfs_attr_pwrite(na, tmp, bufsize, buf); + if (br != bufsize) { + // FIXME: Eeek! We need rollback! (AIA) + if (br >= 0) + errno = EIO; + ntfs_log_perror("Failed to write buffer to bitmap " + "(%lld != %lld). Leaving inconsistent metadata", + (long long)br, (long long)bufsize); + goto free_err_out; + } + + /* Update counters. */ + tmp = (bufsize - firstbyte - lastbyte) << 3; + if (firstbyte) { + firstbyte = 0; + /* + * Re-set the partial first byte so a subsequent write + * of the buffer does not have stale, incorrect bits. + */ + *buf = value ? 0xff : 0; + } + start_bit += tmp; + count -= tmp; + if (bufsize > (tmp = (count + 7) >> 3)) + bufsize = tmp; + + if (lastbyte && count != 0) { + // FIXME: Eeek! BUG! + ntfs_log_error("Last buffer but count is not zero " + "(%lld). Leaving inconsistent metadata.\n", + (long long)count); + errno = EIO; + goto free_err_out; + } + } while (count > 0); + + ret = 0; + +free_err_out: + free(buf); + return ret; +} + +/** + * ntfs_bitmap_set_run - set a run of bits in a bitmap + * @na: attribute containing the bitmap + * @start_bit: first bit to set + * @count: number of bits to set + * + * Set @count bits starting at bit @start_bit in the bitmap described by the + * attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count) +{ + int ret; + + ntfs_log_enter("Set from bit %lld, count %lld\n", + (long long)start_bit, (long long)count); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1); + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_bitmap_clear_run - clear a run of bits in a bitmap + * @na: attribute containing the bitmap + * @start_bit: first bit to clear + * @count: number of bits to clear + * + * Clear @count bits starting at bit @start_bit in the bitmap described by the + * attribute @na. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count) +{ + int ret; + + ntfs_log_enter("Clear from bit %lld, count %lld\n", + (long long)start_bit, (long long)count); + ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0); + ntfs_log_leave("\n"); + return ret; +} + diff --git a/libntfs-3g/bootsect.c b/libntfs-3g/bootsect.c new file mode 100755 index 0000000000000000000000000000000000000000..92c8505df90cd7fe13d4ef1a82a3f1f1adb00a12 --- /dev/null +++ b/libntfs-3g/bootsect.c @@ -0,0 +1,285 @@ +/** + * bootsect.c - Boot sector handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * Copyright (c) 2003-2008 Szabolcs Szakacsits + * Copyright (c) 2005 Yura Pakhuchiy + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "compat.h" +#include "bootsect.h" +#include "debug.h" +#include "logging.h" + +/** + * ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector + * @b: buffer containing putative boot sector to analyze + * @silent: if zero, output progress messages to stderr + * + * Check if the buffer @b contains a valid ntfs boot sector. The buffer @b + * must be at least 512 bytes in size. + * + * If @silent is zero, output progress messages to stderr. Otherwise, do not + * output any messages (except when configured with --enable-debug in which + * case warning/debug messages may be displayed). + * + * Return TRUE if @b contains a valid ntfs boot sector and FALSE if not. + */ +BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) +{ + u32 i; + BOOL ret = FALSE; + + ntfs_log_debug("Beginning bootsector check.\n"); + + ntfs_log_debug("Checking OEMid, NTFS signature.\n"); + if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) { /* "NTFS " */ + ntfs_log_error("NTFS signature is missing.\n"); + goto not_ntfs; + } + + ntfs_log_debug("Checking bytes per sector.\n"); + if (le16_to_cpu(b->bpb.bytes_per_sector) < 256 || + le16_to_cpu(b->bpb.bytes_per_sector) > 4096) { + ntfs_log_error("Unexpected bytes per sector value (%d).\n", + le16_to_cpu(b->bpb.bytes_per_sector)); + goto not_ntfs; + } + + ntfs_log_debug("Checking sectors per cluster.\n"); + switch (b->bpb.sectors_per_cluster) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: + break; + default: + ntfs_log_error("Unexpected sectors per cluster value (%d).\n", + b->bpb.sectors_per_cluster); + goto not_ntfs; + } + + ntfs_log_debug("Checking cluster size.\n"); + i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) * + b->bpb.sectors_per_cluster; + if (i > 65536) { + ntfs_log_error("Unexpected cluster size (%d).\n", i); + goto not_ntfs; + } + + ntfs_log_debug("Checking reserved fields are zero.\n"); + if (le16_to_cpu(b->bpb.reserved_sectors) || + le16_to_cpu(b->bpb.root_entries) || + le16_to_cpu(b->bpb.sectors) || + le16_to_cpu(b->bpb.sectors_per_fat) || + le32_to_cpu(b->bpb.large_sectors) || + b->bpb.fats) { + ntfs_log_error("Reserved fields aren't zero " + "(%d, %d, %d, %d, %d, %d).\n", + le16_to_cpu(b->bpb.reserved_sectors), + le16_to_cpu(b->bpb.root_entries), + le16_to_cpu(b->bpb.sectors), + le16_to_cpu(b->bpb.sectors_per_fat), + le32_to_cpu(b->bpb.large_sectors), + b->bpb.fats); + goto not_ntfs; + } + + ntfs_log_debug("Checking clusters per mft record.\n"); + if ((u8)b->clusters_per_mft_record < 0xe1 || + (u8)b->clusters_per_mft_record > 0xf7) { + switch (b->clusters_per_mft_record) { + case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: + break; + default: + ntfs_log_error("Unexpected clusters per mft record " + "(%d).\n", b->clusters_per_mft_record); + goto not_ntfs; + } + } + + ntfs_log_debug("Checking clusters per index block.\n"); + if ((u8)b->clusters_per_index_record < 0xe1 || + (u8)b->clusters_per_index_record > 0xf7) { + switch (b->clusters_per_index_record) { + case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: + break; + default: + ntfs_log_error("Unexpected clusters per index record " + "(%d).\n", b->clusters_per_index_record); + goto not_ntfs; + } + } + + if (b->end_of_sector_marker != cpu_to_le16(0xaa55)) + ntfs_log_debug("Warning: Bootsector has invalid end of sector " + "marker.\n"); + + ntfs_log_debug("Bootsector check completed successfully.\n"); + + ret = TRUE; +not_ntfs: + return ret; +} + +static const char *last_sector_error = +"HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n" +" or it was not setup correctly (e.g. by not using mdadm --build ...),\n" +" or a wrong device is tried to be mounted,\n" +" or the partition table is corrupt (partition is smaller than NTFS),\n" +" or the NTFS boot sector is corrupt (NTFS size is not valid).\n"; + +/** + * ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector + * @vol: ntfs_volume to setup + * @bs: buffer containing ntfs boot sector to parse + * + * Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the + * obtained values. + * + * Return 0 on success or -1 on error with errno set to the error code EINVAL. + */ +int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) +{ + s64 sectors; + u8 sectors_per_cluster; + s8 c; + + /* We return -1 with errno = EINVAL on error. */ + errno = EINVAL; + + vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector); + vol->sector_size_bits = ffs(vol->sector_size) - 1; + ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size); + ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits); + /* + * The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being + * below or equal the number_of_clusters) really belong in the + * ntfs_boot_sector_is_ntfs but in this way we can just do this once. + */ + sectors_per_cluster = bs->bpb.sectors_per_cluster; + ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster); + if (sectors_per_cluster & (sectors_per_cluster - 1)) { + ntfs_log_error("sectors_per_cluster (%d) is not a power of 2." + "\n", sectors_per_cluster); + return -1; + } + + sectors = sle64_to_cpu(bs->number_of_sectors); + ntfs_log_debug("NumberOfSectors = %lld\n", (long long)sectors); + if (!sectors) { + ntfs_log_error("Volume size is set to zero.\n"); + return -1; + } + if (vol->dev->d_ops->seek(vol->dev, + (sectors - 1) << vol->sector_size_bits, + SEEK_SET) == -1) { + ntfs_log_perror("Failed to read last sector (%lld)", + (long long)(sectors - 1)); + ntfs_log_error("%s", last_sector_error); + return -1; + } + + vol->nr_clusters = sectors >> (ffs(sectors_per_cluster) - 1); + + vol->mft_lcn = sle64_to_cpu(bs->mft_lcn); + vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn); + ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn); + ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn); + if ((vol->mft_lcn < 0 || vol->mft_lcn > vol->nr_clusters) || + (vol->mftmirr_lcn < 0 || vol->mftmirr_lcn > vol->nr_clusters)) { + ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is " + "greater than the number of clusters (%lld).\n", + (long long)vol->mft_lcn, (long long)vol->mftmirr_lcn, + (long long)vol->nr_clusters); + return -1; + } + + vol->cluster_size = sectors_per_cluster * vol->sector_size; + if (vol->cluster_size & (vol->cluster_size - 1)) { + ntfs_log_error("cluster_size (%d) is not a power of 2.\n", + vol->cluster_size); + return -1; + } + vol->cluster_size_bits = ffs(vol->cluster_size) - 1; + /* + * Need to get the clusters per mft record and handle it if it is + * negative. Then calculate the mft_record_size. A value of 0x80 is + * illegal, thus signed char is actually ok! + */ + c = bs->clusters_per_mft_record; + ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size); + ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits); + ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c); + /* + * When clusters_per_mft_record is negative, it means that it is to + * be taken to be the negative base 2 logarithm of the mft_record_size + * min bytes. Then: + * mft_record_size = 2^(-clusters_per_mft_record) bytes. + */ + if (c < 0) + vol->mft_record_size = 1 << -c; + else + vol->mft_record_size = c << vol->cluster_size_bits; + if (vol->mft_record_size & (vol->mft_record_size - 1)) { + ntfs_log_error("mft_record_size (%d) is not a power of 2.\n", + vol->mft_record_size); + return -1; + } + vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; + ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size); + ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits); + /* Same as above for INDX record. */ + c = bs->clusters_per_index_record; + ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c); + if (c < 0) + vol->indx_record_size = 1 << -c; + else + vol->indx_record_size = c << vol->cluster_size_bits; + vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1; + ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size); + ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits); + /* + * Work out the size of the MFT mirror in number of mft records. If the + * cluster size is less than or equal to the size taken by four mft + * records, the mft mirror stores the first four mft records. If the + * cluster size is bigger than the size taken by four mft records, the + * mft mirror contains as many mft records as will fit into one + * cluster. + */ + if (vol->cluster_size <= 4 * vol->mft_record_size) + vol->mftmirr_size = 4; + else + vol->mftmirr_size = vol->cluster_size / vol->mft_record_size; + return 0; +} + diff --git a/libntfs-3g/cache.c b/libntfs-3g/cache.c new file mode 100755 index 0000000000000000000000000000000000000000..2ad8d359e101f341bada80836b858f8ea3885b16 --- /dev/null +++ b/libntfs-3g/cache.c @@ -0,0 +1,606 @@ +/** + * cache.c : deal with LRU caches + * + * Copyright (c) 2008-2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "types.h" +#include "security.h" +#include "cache.h" +#include "misc.h" +#include "logging.h" + +/* + * General functions to deal with LRU caches + * + * The cached data have to be organized in a structure in which + * the first fields must follow a mandatory pattern and further + * fields may contain any fixed size data. They are stored in an + * LRU list. + * + * A compare function must be provided for finding a wanted entry + * in the cache. Another function may be provided for invalidating + * an entry to facilitate multiple invalidation. + * + * These functions never return error codes. When there is a + * shortage of memory, data is simply not cached. + * When there is a hashing bug, hashing is dropped, and sequential + * searches are used. + */ + +/* + * Enter a new hash index, after a new record has been inserted + * + * Do not call when a record has been modified (with no key change) + */ + +static void inserthashindex(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current) +{ + int h; + struct HASH_ENTRY *link; + struct HASH_ENTRY *first; + + if (cache->dohash) { + h = cache->dohash(current); + if ((h >= 0) && (h < cache->max_hash)) { + /* get a free link and insert at top of hash list */ + link = cache->free_hash; + if (link) { + cache->free_hash = link->next; + first = cache->first_hash[h]; + if (first) + link->next = first; + else + link->next = NULL; + link->entry = current; + cache->first_hash[h] = link; + } else { + ntfs_log_error("No more hash entries," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } else { + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } +} + +/* + * Drop a hash index when a record is about to be deleted + */ + +static void drophashindex(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *current, int hash) +{ + struct HASH_ENTRY *link; + struct HASH_ENTRY *previous; + + if (cache->dohash) { + if ((hash >= 0) && (hash < cache->max_hash)) { + /* find the link and unlink */ + link = cache->first_hash[hash]; + previous = (struct HASH_ENTRY*)NULL; + while (link && (link->entry != current)) { + previous = link; + link = link->next; + } + if (link) { + if (previous) + previous->next = link->next; + else + cache->first_hash[hash] = link->next; + link->next = cache->free_hash; + cache->free_hash = link; + } else { + ntfs_log_error("Bad hash list," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } else { + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } +} + +/* + * Fetch an entry from cache + * + * returns the cache entry, or NULL if not available + * The returned entry may be modified, but not freed + */ + +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, cache_compare compare) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *previous; + struct HASH_ENTRY *link; + int h; + + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * locate the entry if present + */ + h = cache->dohash(wanted); + link = cache->first_hash[h]; + while (link && compare(link->entry, wanted)) + link = link->next; + if (link) + current = link->entry; + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list if no hash table + * or if hashing has just failed + */ + current = cache->most_recent_entry; + while (current + && compare(current, wanted)) { + current = current->next; + } + } + if (current) { + previous = current->previous; + cache->hits++; + if (previous) { + /* + * found and not at head of list, unlink from current + * position and relink as head of list + */ + previous->next = current->next; + if (current->next) + current->next->previous + = current->previous; + else + cache->oldest_entry + = current->previous; + current->next = cache->most_recent_entry; + current->previous + = (struct CACHED_GENERIC*)NULL; + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; + } + } + cache->reads++; + } + return (current); +} + +/* + * Enter an inode number into cache + * returns the cache entry or NULL if not possible + */ + +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *before; + struct HASH_ENTRY *link; + int h; + + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link && compare(link->entry, item)) + link = link->next; + if (link) { + current = link->entry; + } + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list to locate the end, + * and find out whether the entry is already in list + * As we normally go to the end, no statistics is + * kept. + */ + current = cache->most_recent_entry; + while (current + && compare(current, item)) { + current = current->next; + } + } + + if (!current) { + /* + * Not in list, get a free entry or reuse the + * last entry, and relink as head of list + * Note : we assume at least three entries, so + * before, previous and first are different when + * an entry is reused. + */ + + if (cache->free_entry) { + current = cache->free_entry; + cache->free_entry = cache->free_entry->next; + if (item->varsize) { + current->variable = ntfs_malloc( + item->varsize); + } else + current->variable = (void*)NULL; + current->varsize = item->varsize; + if (!cache->oldest_entry) + cache->oldest_entry = current; + } else { + /* reusing the oldest entry */ + current = cache->oldest_entry; + before = current->previous; + before->next = (struct CACHED_GENERIC*)NULL; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + if (cache->dofree) + cache->dofree(current); + cache->oldest_entry = current->previous; + if (item->varsize) { + if (current->varsize) + current->variable = realloc( + current->variable, + item->varsize); + else + current->variable = ntfs_malloc( + item->varsize); + } else { + if (current->varsize) + free(current->variable); + current->variable = (void*)NULL; + } + current->varsize = item->varsize; + } + current->next = cache->most_recent_entry; + current->previous = (struct CACHED_GENERIC*)NULL; + if (cache->most_recent_entry) + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; + memcpy(current->payload, item->payload, cache->fixed_size); + if (item->varsize) { + if (current->variable) { + memcpy(current->variable, + item->variable, item->varsize); + } else { + /* + * no more memory for variable part + * recycle entry in free list + * not an error, just uncacheable + */ + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + current = (struct CACHED_GENERIC*)NULL; + } + } else { + current->variable = (void*)NULL; + current->varsize = 0; + } + if (cache->dohash && current) + inserthashindex(cache,current); + } + cache->writes++; + } + return (current); +} + +/* + * Invalidate a cache entry + * The entry is moved to the free entry list + * A specific function may be called for entry deletion + */ + +static void do_invalidate(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current, int flags) +{ + struct CACHED_GENERIC *previous; + + previous = current->previous; + if ((flags & CACHE_FREE) && cache->dofree) + cache->dofree(current); + /* + * Relink into free list + */ + if (current->next) + current->next->previous = current->previous; + else + cache->oldest_entry = current->previous; + if (previous) + previous->next = current->next; + else + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + if (current->variable) + free(current->variable); + current->varsize = 0; + } + + +/* + * Invalidate entries in cache + * + * Several entries may have to be invalidated (at least for inodes + * associated to directories which have been renamed), a different + * compare function may be provided to select entries to invalidate + * + * Returns the number of deleted entries, this can be used by + * the caller to signal a cache corruption if the entry was + * supposed to be found. + */ + +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, cache_compare compare, + int flags) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *next; + struct HASH_ENTRY *link; + int count; + int h; + + current = (struct CACHED_GENERIC*)NULL; + count = 0; + if (cache) { + if (!(flags & CACHE_NOHASH) && cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link) { + if (compare(link->entry, item)) + link = link->next; + else { + current = link->entry; + link = link->next; + if (current) { + drophashindex(cache,current,h); + do_invalidate(cache, + current,flags); + count++; + } + } + } + } + if ((flags & CACHE_NOHASH) || !cache->dohash) { + /* + * Search sequentially in LRU list + */ + current = cache->most_recent_entry; + while (current) { + if (!compare(current, item)) { + next = current->next; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + do_invalidate(cache,current,flags); + current = next; + count++; + } else { + current = current->next; + } + } + } + } + return (count); +} + +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags) +{ + int count; + + count = 0; + if (cache) { + if (cache->dohash) + drophashindex(cache,item,cache->dohash(item)); + do_invalidate(cache,item,flags); + count++; + } + return (count); +} + +/* + * Free memory allocated to a cache + */ + +static void ntfs_free_cache(struct CACHE_HEADER *cache) +{ + struct CACHED_GENERIC *entry; + + if (cache) { + for (entry=cache->most_recent_entry; entry; entry=entry->next) { + if (cache->dofree) + cache->dofree(entry); + if (entry->variable) + free(entry->variable); + } + free(cache); + } +} + +/* + * Create a cache + * + * Returns the cache header, or NULL if the cache could not be created + */ + +static struct CACHE_HEADER *ntfs_create_cache(const char *name, + cache_free dofree, cache_hash dohash, + int full_item_size, + int item_count, int max_hash) +{ + struct CACHE_HEADER *cache; + struct CACHED_GENERIC *pc; + struct CACHED_GENERIC *qc; + struct HASH_ENTRY *ph; + struct HASH_ENTRY *qh; + struct HASH_ENTRY **px; + size_t size; + int i; + + size = sizeof(struct CACHE_HEADER) + item_count*full_item_size; + if (max_hash) + size += item_count*sizeof(struct HASH_ENTRY) + + max_hash*sizeof(struct HASH_ENTRY*); + cache = (struct CACHE_HEADER*)ntfs_malloc(size); + if (cache) { + /* header */ + cache->name = name; + cache->dofree = dofree; + if (dohash && max_hash) { + cache->dohash = dohash; + cache->max_hash = max_hash; + } else { + cache->dohash = (cache_hash)NULL; + cache->max_hash = 0; + } + cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC); + cache->reads = 0; + cache->writes = 0; + cache->hits = 0; + /* chain the data entries, and mark an invalid entry */ + cache->most_recent_entry = (struct CACHED_GENERIC*)NULL; + cache->oldest_entry = (struct CACHED_GENERIC*)NULL; + cache->free_entry = &cache->entry[0]; + pc = &cache->entry[0]; + for (i=0; i<(item_count - 1); i++) { + qc = (struct CACHED_GENERIC*)((char*)pc + + full_item_size); + pc->next = qc; + pc->variable = (void*)NULL; + pc->varsize = 0; + pc = qc; + } + /* special for the last entry */ + pc->next = (struct CACHED_GENERIC*)NULL; + pc->variable = (void*)NULL; + pc->varsize = 0; + + if (max_hash) { + /* chain the hash entries */ + ph = (struct HASH_ENTRY*)(((char*)pc) + full_item_size); + cache->free_hash = ph; + for (i=0; i<(item_count - 1); i++) { + qh = &ph[1]; + ph->next = qh; + ph = qh; + } + /* special for the last entry */ + if (item_count) { + ph->next = (struct HASH_ENTRY*)NULL; + } + /* create and initialize the hash indexes */ + px = (struct HASH_ENTRY**)&ph[1]; + cache->first_hash = px; + for (i=0; i<max_hash; i++) + px[i] = (struct HASH_ENTRY*)NULL; + } else { + cache->free_hash = (struct HASH_ENTRY*)NULL; + cache->first_hash = (struct HASH_ENTRY**)NULL; + } + } + return (cache); +} + +/* + * Create all LRU caches + * + * No error return, if creation is not possible, cacheing will + * just be not available + */ + +void ntfs_create_lru_caches(ntfs_volume *vol) +{ +#if CACHE_INODE_SIZE + /* inode cache */ + vol->xinode_cache = ntfs_create_cache("inode",(cache_free)NULL, + ntfs_dir_inode_hash, sizeof(struct CACHED_INODE), + CACHE_INODE_SIZE, 2*CACHE_INODE_SIZE); +#endif +#if CACHE_NIDATA_SIZE + /* idata cache */ + vol->nidata_cache = ntfs_create_cache("nidata", + ntfs_inode_nidata_free, ntfs_inode_nidata_hash, + sizeof(struct CACHED_NIDATA), + CACHE_NIDATA_SIZE, 2*CACHE_NIDATA_SIZE); +#endif +#if CACHE_LOOKUP_SIZE + /* lookup cache */ + vol->lookup_cache = ntfs_create_cache("lookup", + (cache_free)NULL, ntfs_dir_lookup_hash, + sizeof(struct CACHED_LOOKUP), + CACHE_LOOKUP_SIZE, 2*CACHE_LOOKUP_SIZE); +#endif + vol->securid_cache = ntfs_create_cache("securid",(cache_free)NULL, + (cache_hash)NULL,sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0); +#if CACHE_LEGACY_SIZE + vol->legacy_cache = ntfs_create_cache("legacy",(cache_free)NULL, + (cache_hash)NULL, sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE, 0); +#endif +} + +/* + * Free all LRU caches + */ + +void ntfs_free_lru_caches(ntfs_volume *vol) +{ +#if CACHE_INODE_SIZE + ntfs_free_cache(vol->xinode_cache); +#endif +#if CACHE_NIDATA_SIZE + ntfs_free_cache(vol->nidata_cache); +#endif +#if CACHE_LOOKUP_SIZE + ntfs_free_cache(vol->lookup_cache); +#endif + ntfs_free_cache(vol->securid_cache); +#if CACHE_LEGACY_SIZE + ntfs_free_cache(vol->legacy_cache); +#endif +} diff --git a/libntfs-3g/collate.c b/libntfs-3g/collate.c new file mode 100755 index 0000000000000000000000000000000000000000..5f7a015a14963c190a8ab06a2bd070d809f5e7de --- /dev/null +++ b/libntfs-3g/collate.c @@ -0,0 +1,271 @@ +/** + * collate.c - NTFS collation handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "attrib.h" +#include "index.h" +#include "collate.h" +#include "debug.h" +#include "unistr.h" +#include "logging.h" + +/** + * ntfs_collate_binary - Which of two binary objects should be listed first + * @vol: unused + * @data1: + * @data1_len: + * @data2: + * @data2_len: + * + * Description... + * + * Returns: + */ +static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + + ntfs_log_trace("Entering.\n"); + rc = memcmp(data1, data2, min(data1_len, data2_len)); + if (!rc && (data1_len != data2_len)) { + if (data1_len < data2_len) + rc = -1; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_ntofs_ulong - Which of two long ints should be listed first + * @vol: unused + * @data1: + * @data1_len: + * @data2: + * @data2_len: + * + * Description... + * + * Returns: + */ +static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + u32 d1, d2; + + ntfs_log_trace("Entering.\n"); + if (data1_len != data2_len || data1_len != 4) { + ntfs_log_error("data1_len or/and data2_len not equal to 4.\n"); + return NTFS_COLLATION_ERROR; + } + d1 = le32_to_cpup(data1); + d2 = le32_to_cpup(data2); + if (d1 < d2) + rc = -1; + else { + if (d1 == d2) + rc = 0; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_ntofs_ulongs - Which of two le32 arrays should be listed first + * + * Returns: -1, 0 or 1 depending of how the arrays compare + */ + +static int ntfs_collate_ntofs_ulongs(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + int len; + const le32 *p1, *p2; + u32 d1, d2; + + ntfs_log_trace("Entering.\n"); + if ((data1_len != data2_len) || (data1_len <= 0) || (data1_len & 3)) { + ntfs_log_error("data1_len or data2_len not valid\n"); + return NTFS_COLLATION_ERROR; + } + p1 = (const le32*)data1; + p2 = (const le32*)data2; + len = data1_len; + do { + d1 = le32_to_cpup(p1); + p1++; + d2 = le32_to_cpup(p2); + p2++; + } while ((d1 == d2) && ((len -= 4) > 0)); + if (d1 < d2) + rc = -1; + else { + if (d1 == d2) + rc = 0; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_ntofs_security_hash - Which of two security descriptors + * should be listed first + * @vol: unused + * @data1: + * @data1_len: + * @data2: + * @data2_len: + * + * JPA compare two security hash keys made of two unsigned le32 + * + * Returns: -1, 0 or 1 depending of how the keys compare + */ +static int ntfs_collate_ntofs_security_hash(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + u32 d1, d2; + const le32 *p1, *p2; + + ntfs_log_trace("Entering.\n"); + if (data1_len != data2_len || data1_len != 8) { + ntfs_log_error("data1_len or/and data2_len not equal to 8.\n"); + return NTFS_COLLATION_ERROR; + } + p1 = (const le32*)data1; + p2 = (const le32*)data2; + d1 = le32_to_cpup(p1); + d2 = le32_to_cpup(p2); + if (d1 < d2) + rc = -1; + else { + if (d1 > d2) + rc = 1; + else { + p1++; + p2++; + d1 = le32_to_cpup(p1); + d2 = le32_to_cpup(p2); + if (d1 < d2) + rc = -1; + else { + if (d1 > d2) + rc = 1; + else + rc = 0; + } + } + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/** + * ntfs_collate_file_name - Which of two filenames should be listed first + * @vol: + * @data1: + * @data1_len: unused + * @data2: + * @data2_len: unused + * + * Description... + * + * Returns: + */ +static int ntfs_collate_file_name(ntfs_volume *vol, + const void *data1, const int data1_len __attribute__((unused)), + const void *data2, const int data2_len __attribute__((unused))) +{ + const FILE_NAME_ATTR *file_name_attr1; + const FILE_NAME_ATTR *file_name_attr2; + int rc; + + ntfs_log_trace("Entering.\n"); + file_name_attr1 = (const FILE_NAME_ATTR*)data1; + file_name_attr2 = (const FILE_NAME_ATTR*)data2; + rc = ntfs_names_full_collate( + (ntfschar*)&file_name_attr1->file_name, + file_name_attr1->file_name_length, + (ntfschar*)&file_name_attr2->file_name, + file_name_attr2->file_name_length, + CASE_SENSITIVE, vol->upcase, vol->upcase_len); + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + +/* + * Get a pointer to appropriate collation function. + * + * Returns NULL if the needed function is not implemented + */ + +COLLATE ntfs_get_collate_function(COLLATION_RULES cr) +{ + COLLATE collate; + + switch (cr) { + case COLLATION_BINARY : + collate = ntfs_collate_binary; + break; + case COLLATION_FILE_NAME : + collate = ntfs_collate_file_name; + break; + case COLLATION_NTOFS_SECURITY_HASH : + collate = ntfs_collate_ntofs_security_hash; + break; + case COLLATION_NTOFS_ULONG : + collate = ntfs_collate_ntofs_ulong; + break; + case COLLATION_NTOFS_ULONGS : + collate = ntfs_collate_ntofs_ulongs; + break; + default : + errno = EOPNOTSUPP; + collate = (COLLATE)NULL; + break; + } + return (collate); +} diff --git a/libntfs-3g/compat.c b/libntfs-3g/compat.c new file mode 100755 index 0000000000000000000000000000000000000000..63114a48707886ed5836ceb6b0e1d7d3b6432776 --- /dev/null +++ b/libntfs-3g/compat.c @@ -0,0 +1,250 @@ +/** + * compat.c - Tweaks for Windows compatibility + * + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2002-2004 Anton Altaparmakov + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "compat.h" + +#ifndef HAVE_FFS +/** + * ffs - Find the first set bit in an int + * @x: + * + * Description... + * + * Returns: + */ +int ffs(int x) +{ + int r = 1; + + if (!x) + return 0; + if (!(x & 0xffff)) { + x >>= 16; + r += 16; + } + if (!(x & 0xff)) { + x >>= 8; + r += 8; + } + if (!(x & 0xf)) { + x >>= 4; + r += 4; + } + if (!(x & 3)) { + x >>= 2; + r += 2; + } + if (!(x & 1)) { + x >>= 1; + r += 1; + } + return r; +} +#endif /* HAVE_FFS */ + +#ifndef HAVE_DAEMON +/* ************************************************************ + * From: src.opensolaris.org + * src/lib/libresolv2/common/bsd/daemon.c + */ +/* + * Copyright (c) 1997-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +int daemon(int nochdir, int noclose) { + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { + (void)dup2(fd, 0); + (void)dup2(fd, 1); + (void)dup2(fd, 2); + if (fd > 2) + (void)close (fd); + } + return (0); +} +/* + * End: src/lib/libresolv2/common/bsd/daemon.c + *************************************************************/ +#endif /* HAVE_DAEMON */ + +#ifndef HAVE_STRSEP +/* ************************************************************ + * From: src.opensolaris.org + * src/lib/libresolv2/common/bsd/strsep.c + */ +/* + * Copyright (c) 1997, by Sun Microsystems, Inc. + * All rights reserved. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "strsep.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char *strsep(char **stringp, const char *delim) { + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +/* + * End: src/lib/libresolv2/common/bsd/strsep.c + *************************************************************/ +#endif /* HAVE_STRSEP */ + diff --git a/libntfs-3g/compress.c b/libntfs-3g/compress.c new file mode 100755 index 0000000000000000000000000000000000000000..f1070aa273d390eb493b482160a4333b492cf5d9 --- /dev/null +++ b/libntfs-3g/compress.c @@ -0,0 +1,1938 @@ +/** + * compress.c - Compressed attribute handling code. Originated from the Linux-NTFS + * project. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2009-2014 Jean-Pierre Andre + * Copyright (c) 2014 Eric Biggers + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "attrib.h" +#include "debug.h" +#include "volume.h" +#include "types.h" +#include "layout.h" +#include "runlist.h" +#include "compress.h" +#include "lcnalloc.h" +#include "logging.h" +#include "misc.h" + +#undef le16_to_cpup +/* the standard le16_to_cpup() crashes for unaligned data on some processors */ +#define le16_to_cpup(p) (*(u8*)(p) + (((u8*)(p))[1] << 8)) + +/** + * enum ntfs_compression_constants - constants used in the compression code + */ +typedef enum { + /* Token types and access mask. */ + NTFS_SYMBOL_TOKEN = 0, + NTFS_PHRASE_TOKEN = 1, + NTFS_TOKEN_MASK = 1, + + /* Compression sub-block constants. */ + NTFS_SB_SIZE_MASK = 0x0fff, + NTFS_SB_SIZE = 0x1000, + NTFS_SB_IS_COMPRESSED = 0x8000, +} ntfs_compression_constants; + +/* Match length at or above which ntfs_best_match() will stop searching for + * longer matches. */ +#define NICE_MATCH_LEN 18 + +/* Maximum number of potential matches that ntfs_best_match() will consider at + * each position. */ +#define MAX_SEARCH_DEPTH 24 + +/* log base 2 of the number of entries in the hash table for match-finding. */ +#define HASH_SHIFT 14 + +/* Constant for the multiplicative hash function. */ +#define HASH_MULTIPLIER 0x1E35A7BD + +struct COMPRESS_CONTEXT { + const unsigned char *inbuf; + int bufsize; + int size; + int rel; + int mxsz; + s16 head[1 << HASH_SHIFT]; + s16 prev[NTFS_SB_SIZE]; +} ; + +/* + * Hash the next 3-byte sequence in the input buffer + */ +static inline unsigned int ntfs_hash(const u8 *p) +{ + u32 str; + u32 hash; + +#if defined(__i386__) || defined(__x86_64__) + /* Unaligned access allowed, and little endian CPU. + * Callers ensure that at least 4 (not 3) bytes are remaining. */ + str = *(const u32 *)p & 0xFFFFFF; +#else + str = ((u32)p[0] << 0) | ((u32)p[1] << 8) | ((u32)p[2] << 16); +#endif + + hash = str * HASH_MULTIPLIER; + + /* High bits are more random than the low bits. */ + return hash >> (32 - HASH_SHIFT); +} + +/* + * Search for the longest sequence matching current position + * + * A hash table, each entry of which points to a chain of sequence + * positions sharing the corresponding hash code, is maintained to speed up + * searching for matches. To maintain the hash table, either + * ntfs_best_match() or ntfs_skip_position() has to be called for each + * consecutive position. + * + * This function is heavily used; it has to be optimized carefully. + * + * This function sets pctx->size and pctx->rel to the length and offset, + * respectively, of the longest match found. + * + * The minimum match length is assumed to be 3, and the maximum match + * length is assumed to be pctx->mxsz. If this function produces + * pctx->size < 3, then no match was found. + * + * Note: for the following reasons, this function is not guaranteed to find + * *the* longest match up to pctx->mxsz: + * + * (1) If this function finds a match of NICE_MATCH_LEN bytes or greater, + * it ends early because a match this long is good enough and it's not + * worth spending more time searching. + * + * (2) If this function considers MAX_SEARCH_DEPTH matches with a single + * position, it ends early and returns the longest match found so far. + * This saves a lot of time on degenerate inputs. + */ +static void ntfs_best_match(struct COMPRESS_CONTEXT *pctx, const int i, + int best_len) +{ + const u8 * const inbuf = pctx->inbuf; + const u8 * const strptr = &inbuf[i]; /* String we're matching against */ + s16 * const prev = pctx->prev; + const int max_len = min(pctx->bufsize - i, pctx->mxsz); + const int nice_len = min(NICE_MATCH_LEN, max_len); + int depth_remaining = MAX_SEARCH_DEPTH; + const u8 *best_matchptr = strptr; + unsigned int hash; + s16 cur_match; + const u8 *matchptr; + int len; + + if (max_len < 4) + goto out; + + /* Insert the current sequence into the appropriate hash chain. */ + hash = ntfs_hash(strptr); + cur_match = pctx->head[hash]; + prev[i] = cur_match; + pctx->head[hash] = i; + + if (best_len >= max_len) { + /* Lazy match is being attempted, but there aren't enough length + * bits remaining to code a longer match. */ + goto out; + } + + /* Search the appropriate hash chain for matches. */ + + for (; cur_match >= 0 && depth_remaining--; + cur_match = prev[cur_match]) + { + + matchptr = &inbuf[cur_match]; + + /* Considering the potential match at 'matchptr': is it longer + * than 'best_len'? + * + * The bytes at index 'best_len' are the most likely to differ, + * so check them first. + * + * The bytes at indices 'best_len - 1' and '0' are less + * important to check separately. But doing so still gives a + * slight performance improvement, at least on x86_64, probably + * because they create separate branches for the CPU to predict + * independently of the branches in the main comparison loops. + */ + if (matchptr[best_len] != strptr[best_len] || + matchptr[best_len - 1] != strptr[best_len - 1] || + matchptr[0] != strptr[0]) + goto next_match; + + for (len = 1; len < best_len - 1; len++) + if (matchptr[len] != strptr[len]) + goto next_match; + + /* The match is the longest found so far --- + * at least 'best_len' + 1 bytes. Continue extending it. */ + + best_matchptr = matchptr; + + do { + if (++best_len >= nice_len) { + /* 'nice_len' reached; don't waste time + * searching for longer matches. Extend the + * match as far as possible and terminate the + * search. */ + while (best_len < max_len && + (best_matchptr[best_len] == + strptr[best_len])) + { + best_len++; + } + goto out; + } + } while (best_matchptr[best_len] == strptr[best_len]); + + /* Found a longer match, but 'nice_len' not yet reached. */ + + next_match: + /* Continue to next match in the chain. */ + ; + } + + /* Reached end of chain, or ended early due to reaching the maximum + * search depth. */ + +out: + /* Return the longest match we were able to find. */ + pctx->size = best_len; + pctx->rel = best_matchptr - strptr; /* given as a negative number! */ +} + +/* + * Advance the match-finder, but don't search for matches. + */ +static void ntfs_skip_position(struct COMPRESS_CONTEXT *pctx, const int i) +{ + unsigned int hash; + + if (pctx->bufsize - i < 4) + return; + + /* Insert the current sequence into the appropriate hash chain. */ + hash = ntfs_hash(pctx->inbuf + i); + pctx->prev[i] = pctx->head[hash]; + pctx->head[hash] = i; +} + +/* + * Compress a 4096-byte block + * + * Returns a header of two bytes followed by the compressed data. + * If compression is not effective, the header and an uncompressed + * block is returned. + * + * Note : two bytes may be output before output buffer overflow + * is detected, so a 4100-bytes output buffer must be reserved. + * + * Returns the size of the compressed block, including the + * header (minimal size is 2, maximum size is 4098) + * 0 if an error has been met. + */ + +static unsigned int ntfs_compress_block(const char *inbuf, const int bufsize, + char *outbuf) +{ + struct COMPRESS_CONTEXT *pctx; + int i; /* current position */ + int j; /* end of best match from current position */ + int k; /* end of best match from next position */ + int offs; /* offset to best match */ + int bp; /* bits to store offset */ + int bp_cur; /* saved bits to store offset at current position */ + int mxoff; /* max match offset : 1 << bp */ + unsigned int xout; + unsigned int q; /* aggregated offset and size */ + int have_match; /* do we have a match at the current position? */ + char *ptag; /* location reserved for a tag */ + int tag; /* current value of tag */ + int ntag; /* count of bits still undefined in tag */ + + pctx = ntfs_malloc(sizeof(struct COMPRESS_CONTEXT)); + if (!pctx) { + errno = ENOMEM; + return 0; + } + + /* All hash chains start as empty. The special value '-1' indicates the + * end of each hash chain. */ + memset(pctx->head, 0xFF, sizeof(pctx->head)); + + pctx->inbuf = (const unsigned char*)inbuf; + pctx->bufsize = bufsize; + xout = 2; + i = 0; + bp = 4; + mxoff = 1 << bp; + pctx->mxsz = (1 << (16 - bp)) + 2; + have_match = 0; + tag = 0; + ntag = 8; + ptag = &outbuf[xout++]; + + while ((i < bufsize) && (xout < (NTFS_SB_SIZE + 2))) { + + /* This implementation uses "lazy" parsing: it always chooses + * the longest match, unless the match at the next position is + * longer. This is the same strategy used by the high + * compression modes of zlib. */ + + if (!have_match) { + /* Find the longest match at the current position. But + * first adjust the maximum match length if needed. + * (This loop might need to run more than one time in + * the case that we just output a long match.) */ + while (mxoff < i) { + bp++; + mxoff <<= 1; + pctx->mxsz = (pctx->mxsz + 2) >> 1; + } + ntfs_best_match(pctx, i, 2); + } + + if (pctx->size >= 3) { + + /* Found a match at the current position. */ + + j = i + pctx->size; + bp_cur = bp; + offs = pctx->rel; + + if (pctx->size >= NICE_MATCH_LEN) { + + /* Choose long matches immediately. */ + + q = (~offs << (16 - bp_cur)) + (j - i - 3); + outbuf[xout++] = q & 255; + outbuf[xout++] = (q >> 8) & 255; + tag |= (1 << (8 - ntag)); + + if (j == bufsize) { + /* Shortcut if the match extends to the + * end of the buffer. */ + i = j; + --ntag; + break; + } + i += 1; + do { + ntfs_skip_position(pctx, i); + } while (++i != j); + have_match = 0; + } else { + /* Check for a longer match at the next + * position. */ + + /* Doesn't need to be while() since we just + * adjusted the maximum match length at the + * previous position. */ + if (mxoff < i + 1) { + bp++; + mxoff <<= 1; + pctx->mxsz = (pctx->mxsz + 2) >> 1; + } + ntfs_best_match(pctx, i + 1, pctx->size); + k = i + 1 + pctx->size; + + if (k > (j + 1)) { + /* Next match is longer. + * Output a literal. */ + outbuf[xout++] = inbuf[i++]; + have_match = 1; + } else { + /* Next match isn't longer. + * Output the current match. */ + q = (~offs << (16 - bp_cur)) + + (j - i - 3); + outbuf[xout++] = q & 255; + outbuf[xout++] = (q >> 8) & 255; + tag |= (1 << (8 - ntag)); + + /* The minimum match length is 3, and + * we've run two bytes through the + * matchfinder already. So the minimum + * number of positions we need to skip + * is 1. */ + i += 2; + do { + ntfs_skip_position(pctx, i); + } while (++i != j); + have_match = 0; + } + } + } else { + /* No match at current position. Output a literal. */ + outbuf[xout++] = inbuf[i++]; + have_match = 0; + } + + /* Store the tag if fully used. */ + if (!--ntag) { + *ptag = tag; + ntag = 8; + ptag = &outbuf[xout++]; + tag = 0; + } + } + + /* Store the last tag if partially used. */ + if (ntag == 8) + xout--; + else + *ptag = tag; + + /* Determine whether to store the data compressed or uncompressed. */ + + if ((i >= bufsize) && (xout < (NTFS_SB_SIZE + 2))) { + /* Compressed. */ + outbuf[0] = (xout - 3) & 255; + outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15); + } else { + /* Uncompressed. */ + memcpy(&outbuf[2], inbuf, bufsize); + if (bufsize < NTFS_SB_SIZE) + memset(&outbuf[bufsize + 2], 0, NTFS_SB_SIZE - bufsize); + outbuf[0] = 0xff; + outbuf[1] = 0x3f; + xout = NTFS_SB_SIZE + 2; + } + + /* Free the compression context and return the total number of bytes + * written to 'outbuf'. */ + free(pctx); + return (xout); +} + +/** + * ntfs_decompress - decompress a compression block into an array of pages + * @dest: buffer to which to write the decompressed data + * @dest_size: size of buffer @dest in bytes + * @cb_start: compression block to decompress + * @cb_size: size of compression block @cb_start in bytes + * + * This decompresses the compression block @cb_start into the destination + * buffer @dest. + * + * @cb_start is a pointer to the compression block which needs decompressing + * and @cb_size is the size of @cb_start in bytes (8-64kiB). + * + * Return 0 if success or -EOVERFLOW on error in the compressed stream. + */ +static int ntfs_decompress(u8 *dest, const u32 dest_size, + u8 *const cb_start, const u32 cb_size) +{ + /* + * Pointers into the compressed data, i.e. the compression block (cb), + * and the therein contained sub-blocks (sb). + */ + u8 *cb_end = cb_start + cb_size; /* End of cb. */ + u8 *cb = cb_start; /* Current position in cb. */ + u8 *cb_sb_start = cb; /* Beginning of the current sb in the cb. */ + u8 *cb_sb_end; /* End of current sb / beginning of next sb. */ + /* Variables for uncompressed data / destination. */ + u8 *dest_end = dest + dest_size; /* End of dest buffer. */ + u8 *dest_sb_start; /* Start of current sub-block in dest. */ + u8 *dest_sb_end; /* End of current sb in dest. */ + /* Variables for tag and token parsing. */ + u8 tag; /* Current tag. */ + int token; /* Loop counter for the eight tokens in tag. */ + + ntfs_log_trace("Entering, cb_size = 0x%x.\n", (unsigned)cb_size); +do_next_sb: + ntfs_log_debug("Beginning sub-block at offset = %d in the cb.\n", + (int)(cb - cb_start)); + /* + * Have we reached the end of the compression block or the end of the + * decompressed data? The latter can happen for example if the current + * position in the compression block is one byte before its end so the + * first two checks do not detect it. + */ + if (cb == cb_end || !le16_to_cpup((le16*)cb) || dest == dest_end) { + ntfs_log_debug("Completed. Returning success (0).\n"); + return 0; + } + /* Setup offset for the current sub-block destination. */ + dest_sb_start = dest; + dest_sb_end = dest + NTFS_SB_SIZE; + /* Check that we are still within allowed boundaries. */ + if (dest_sb_end > dest_end) + goto return_overflow; + /* Does the minimum size of a compressed sb overflow valid range? */ + if (cb + 6 > cb_end) + goto return_overflow; + /* Setup the current sub-block source pointers and validate range. */ + cb_sb_start = cb; + cb_sb_end = cb_sb_start + (le16_to_cpup((le16*)cb) & NTFS_SB_SIZE_MASK) + + 3; + if (cb_sb_end > cb_end) + goto return_overflow; + /* Now, we are ready to process the current sub-block (sb). */ + if (!(le16_to_cpup((le16*)cb) & NTFS_SB_IS_COMPRESSED)) { + ntfs_log_debug("Found uncompressed sub-block.\n"); + /* This sb is not compressed, just copy it into destination. */ + /* Advance source position to first data byte. */ + cb += 2; + /* An uncompressed sb must be full size. */ + if (cb_sb_end - cb != NTFS_SB_SIZE) + goto return_overflow; + /* Copy the block and advance the source position. */ + memcpy(dest, cb, NTFS_SB_SIZE); + cb += NTFS_SB_SIZE; + /* Advance destination position to next sub-block. */ + dest += NTFS_SB_SIZE; + goto do_next_sb; + } + ntfs_log_debug("Found compressed sub-block.\n"); + /* This sb is compressed, decompress it into destination. */ + /* Forward to the first tag in the sub-block. */ + cb += 2; +do_next_tag: + if (cb == cb_sb_end) { + /* Check if the decompressed sub-block was not full-length. */ + if (dest < dest_sb_end) { + int nr_bytes = dest_sb_end - dest; + + ntfs_log_debug("Filling incomplete sub-block with zeroes.\n"); + /* Zero remainder and update destination position. */ + memset(dest, 0, nr_bytes); + dest += nr_bytes; + } + /* We have finished the current sub-block. */ + goto do_next_sb; + } + /* Check we are still in range. */ + if (cb > cb_sb_end || dest > dest_sb_end) + goto return_overflow; + /* Get the next tag and advance to first token. */ + tag = *cb++; + /* Parse the eight tokens described by the tag. */ + for (token = 0; token < 8; token++, tag >>= 1) { + u16 lg, pt, length, max_non_overlap; + register u16 i; + u8 *dest_back_addr; + + /* Check if we are done / still in range. */ + if (cb >= cb_sb_end || dest > dest_sb_end) + break; + /* Determine token type and parse appropriately.*/ + if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) { + /* + * We have a symbol token, copy the symbol across, and + * advance the source and destination positions. + */ + *dest++ = *cb++; + /* Continue with the next token. */ + continue; + } + /* + * We have a phrase token. Make sure it is not the first tag in + * the sb as this is illegal and would confuse the code below. + */ + if (dest == dest_sb_start) + goto return_overflow; + /* + * Determine the number of bytes to go back (p) and the number + * of bytes to copy (l). We use an optimized algorithm in which + * we first calculate log2(current destination position in sb), + * which allows determination of l and p in O(1) rather than + * O(n). We just need an arch-optimized log2() function now. + */ + lg = 0; + for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1) + lg++; + /* Get the phrase token into i. */ + pt = le16_to_cpup((le16*)cb); + /* + * Calculate starting position of the byte sequence in + * the destination using the fact that p = (pt >> (12 - lg)) + 1 + * and make sure we don't go too far back. + */ + dest_back_addr = dest - (pt >> (12 - lg)) - 1; + if (dest_back_addr < dest_sb_start) + goto return_overflow; + /* Now calculate the length of the byte sequence. */ + length = (pt & (0xfff >> lg)) + 3; + /* Verify destination is in range. */ + if (dest + length > dest_sb_end) + goto return_overflow; + /* The number of non-overlapping bytes. */ + max_non_overlap = dest - dest_back_addr; + if (length <= max_non_overlap) { + /* The byte sequence doesn't overlap, just copy it. */ + memcpy(dest, dest_back_addr, length); + /* Advance destination pointer. */ + dest += length; + } else { + /* + * The byte sequence does overlap, copy non-overlapping + * part and then do a slow byte by byte copy for the + * overlapping part. Also, advance the destination + * pointer. + */ + memcpy(dest, dest_back_addr, max_non_overlap); + dest += max_non_overlap; + dest_back_addr += max_non_overlap; + length -= max_non_overlap; + while (length--) + *dest++ = *dest_back_addr++; + } + /* Advance source position and continue with the next token. */ + cb += 2; + } + /* No tokens left in the current tag. Continue with the next tag. */ + goto do_next_tag; +return_overflow: + errno = EOVERFLOW; + ntfs_log_perror("Failed to decompress file"); + return -1; +} + +/** + * ntfs_is_cb_compressed - internal function, do not use + * + * This is a very specialised function determining if a cb is compressed or + * uncompressed. It is assumed that checking for a sparse cb has already been + * performed and that the cb is not sparse. It makes all sorts of other + * assumptions as well and hence it is not useful anywhere other than where it + * is used at the moment. Please, do not make this function available for use + * outside of compress.c as it is bound to confuse people and not do what they + * want. + * + * Return TRUE on errors so that the error will be detected later on in the + * code. Might be a bit confusing to debug but there really should never be + * errors coming from here. + */ +static BOOL ntfs_is_cb_compressed(ntfs_attr *na, runlist_element *rl, + VCN cb_start_vcn, int cb_clusters) +{ + /* + * The simplest case: the run starting at @cb_start_vcn contains + * @cb_clusters clusters which are all not sparse, thus the cb is not + * compressed. + */ +restart: + cb_clusters -= rl->length - (cb_start_vcn - rl->vcn); + while (cb_clusters > 0) { + /* Go to the next run. */ + rl++; + /* Map the next runlist fragment if it is not mapped. */ + if (rl->lcn < LCN_HOLE || !rl->length) { + cb_start_vcn = rl->vcn; + rl = ntfs_attr_find_vcn(na, rl->vcn); + if (!rl || rl->lcn < LCN_HOLE || !rl->length) + return TRUE; + /* + * If the runs were merged need to deal with the + * resulting partial run so simply restart. + */ + if (rl->vcn < cb_start_vcn) + goto restart; + } + /* If the current run is sparse, the cb is compressed. */ + if (rl->lcn == LCN_HOLE) + return TRUE; + /* If the whole cb is not sparse, it is not compressed. */ + if (rl->length >= cb_clusters) + return FALSE; + cb_clusters -= rl->length; + }; + /* All cb_clusters were not sparse thus the cb is not compressed. */ + return FALSE; +} + +/** + * ntfs_compressed_attr_pread - read from a compressed attribute + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer + * + * NOTE: You probably want to be using attrib.c::ntfs_attr_pread() instead. + * + * This function will read @count bytes starting at offset @pos from the + * compressed ntfs attribute @na into the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number + * is lower than @count this means that the read reached end of file or that + * an error was encountered during the read so that the read is partial. + * 0 means end of file or nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + */ +s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b) +{ + s64 br, to_read, ofs, total, total2; + u64 cb_size_mask; + VCN start_vcn, vcn, end_vcn; + ntfs_volume *vol; + runlist_element *rl; + u8 *dest, *cb, *cb_pos, *cb_end; + u32 cb_size; + int err; + ATTR_FLAGS data_flags; + FILE_ATTR_FLAGS compression; + unsigned int nr_cbs, cb_clusters; + + ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count 0x%llx.\n", + (unsigned long long)na->ni->mft_no, na->type, + (long long)pos, (long long)count); + data_flags = na->data_flags; + compression = na->ni->flags & FILE_ATTR_COMPRESSED; + if (!na || !na->ni || !na->ni->vol || !b + || ((data_flags & ATTR_COMPRESSION_MASK) + != ATTR_IS_COMPRESSED) + || pos < 0 || count < 0) { + errno = EINVAL; + return -1; + } + /* + * Encrypted attributes are not supported. We return access denied, + * which is what Windows NT4 does, too. + */ + if (NAttrEncrypted(na)) { + errno = EACCES; + return -1; + } + if (!count) + return 0; + /* Truncate reads beyond end of attribute. */ + if (pos + count > na->data_size) { + if (pos >= na->data_size) { + return 0; + } + count = na->data_size - pos; + } + /* If it is a resident attribute, simply use ntfs_attr_pread(). */ + if (!NAttrNonResident(na)) + return ntfs_attr_pread(na, pos, count, b); + total = total2 = 0; + /* Zero out reads beyond initialized size. */ + if (pos + count > na->initialized_size) { + if (pos >= na->initialized_size) { + memset(b, 0, count); + return count; + } + total2 = pos + count - na->initialized_size; + count -= total2; + memset((u8*)b + count, 0, total2); + } + vol = na->ni->vol; + cb_size = na->compression_block_size; + cb_size_mask = cb_size - 1UL; + cb_clusters = na->compression_block_clusters; + + /* Need a temporary buffer for each loaded compression block. */ + cb = (u8*)ntfs_malloc(cb_size); + if (!cb) + return -1; + + /* Need a temporary buffer for each uncompressed block. */ + dest = (u8*)ntfs_malloc(cb_size); + if (!dest) { + free(cb); + return -1; + } + /* + * The first vcn in the first compression block (cb) which we need to + * decompress. + */ + start_vcn = (pos & ~cb_size_mask) >> vol->cluster_size_bits; + /* Offset in the uncompressed cb at which to start reading data. */ + ofs = pos & cb_size_mask; + /* + * The first vcn in the cb after the last cb which we need to + * decompress. + */ + end_vcn = ((pos + count + cb_size - 1) & ~cb_size_mask) >> + vol->cluster_size_bits; + /* Number of compression blocks (cbs) in the wanted vcn range. */ + nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits >> + na->compression_block_size_bits; + cb_end = cb + cb_size; +do_next_cb: + nr_cbs--; + cb_pos = cb; + vcn = start_vcn; + start_vcn += cb_clusters; + + /* Check whether the compression block is sparse. */ + rl = ntfs_attr_find_vcn(na, vcn); + if (!rl || rl->lcn < LCN_HOLE) { + free(cb); + free(dest); + if (total) + return total; + /* FIXME: Do we want EIO or the error code? (AIA) */ + errno = EIO; + return -1; + } + if (rl->lcn == LCN_HOLE) { + /* Sparse cb, zero out destination range overlapping the cb. */ + ntfs_log_debug("Found sparse compression block.\n"); + to_read = min(count, cb_size - ofs); + memset(b, 0, to_read); + ofs = 0; + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + } else if (!ntfs_is_cb_compressed(na, rl, vcn, cb_clusters)) { + s64 tdata_size, tinitialized_size; + /* + * Uncompressed cb, read it straight into the destination range + * overlapping the cb. + */ + ntfs_log_debug("Found uncompressed compression block.\n"); + /* + * Read the uncompressed data into the destination buffer. + * NOTE: We cheat a little bit here by marking the attribute as + * not compressed in the ntfs_attr structure so that we can + * read the data by simply using ntfs_attr_pread(). (-8 + * NOTE: we have to modify data_size and initialized_size + * temporarily as well... + */ + to_read = min(count, cb_size - ofs); + ofs += vcn << vol->cluster_size_bits; + NAttrClearCompressed(na); + na->data_flags &= ~ATTR_COMPRESSION_MASK; + tdata_size = na->data_size; + tinitialized_size = na->initialized_size; + na->data_size = na->initialized_size = na->allocated_size; + do { + br = ntfs_attr_pread(na, ofs, to_read, b); + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read an" + " uncompressed cluster," + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)ofs); + errno = EIO; + } + err = errno; + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return br; + } + total += br; + count -= br; + b = (u8*)b + br; + to_read -= br; + ofs += br; + } while (to_read > 0); + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + ofs = 0; + } else { + s64 tdata_size, tinitialized_size; + u32 decompsz; + + /* + * Compressed cb, decompress it into the temporary buffer, then + * copy the data to the destination range overlapping the cb. + */ + ntfs_log_debug("Found compressed compression block.\n"); + /* + * Read the compressed data into the temporary buffer. + * NOTE: We cheat a little bit here by marking the attribute as + * not compressed in the ntfs_attr structure so that we can + * read the raw, compressed data by simply using + * ntfs_attr_pread(). (-8 + * NOTE: We have to modify data_size and initialized_size + * temporarily as well... + */ + to_read = cb_size; + NAttrClearCompressed(na); + na->data_flags &= ~ATTR_COMPRESSION_MASK; + tdata_size = na->data_size; + tinitialized_size = na->initialized_size; + na->data_size = na->initialized_size = na->allocated_size; + do { + br = ntfs_attr_pread(na, + (vcn << vol->cluster_size_bits) + + (cb_pos - cb), to_read, cb_pos); + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read a" + " compressed cluster, " + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)(vcn << vol->cluster_size_bits)); + errno = EIO; + } + err = errno; + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return br; + } + cb_pos += br; + to_read -= br; + } while (to_read > 0); + na->data_size = tdata_size; + na->initialized_size = tinitialized_size; + na->ni->flags |= compression; + na->data_flags = data_flags; + /* Just a precaution. */ + if (cb_pos + 2 <= cb_end) + *(u16*)cb_pos = 0; + ntfs_log_debug("Successfully read the compression block.\n"); + /* Do not decompress beyond the requested block */ + to_read = min(count, cb_size - ofs); + decompsz = ((ofs + to_read - 1) | (NTFS_SB_SIZE - 1)) + 1; + if (ntfs_decompress(dest, decompsz, cb, cb_size) < 0) { + err = errno; + free(cb); + free(dest); + if (total) + return total; + errno = err; + return -1; + } + memcpy(b, dest + ofs, to_read); + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + ofs = 0; + } + /* Do we have more work to do? */ + if (nr_cbs) + goto do_next_cb; + /* We no longer need the buffers. */ + free(cb); + free(dest); + /* Return number of bytes read. */ + return total + total2; +} + +/* + * Read data from a set of clusters + * + * Returns the amount of data read + */ + +static u32 read_clusters(ntfs_volume *vol, const runlist_element *rl, + s64 offs, u32 to_read, char *inbuf) +{ + u32 count; + int xgot; + u32 got; + s64 xpos; + BOOL first; + char *xinbuf; + const runlist_element *xrl; + + got = 0; + xrl = rl; + xinbuf = inbuf; + first = TRUE; + do { + count = xrl->length << vol->cluster_size_bits; + xpos = xrl->lcn << vol->cluster_size_bits; + if (first) { + count -= offs; + xpos += offs; + } + if ((to_read - got) < count) + count = to_read - got; + xgot = ntfs_pread(vol->dev, xpos, count, xinbuf); + if (xgot == (int)count) { + got += count; + xpos += count; + xinbuf += count; + xrl++; + } + first = FALSE; + } while ((xgot == (int)count) && (got < to_read)); + return (got); +} + +/* + * Write data to a set of clusters + * + * Returns the amount of data written + */ + +static s32 write_clusters(ntfs_volume *vol, const runlist_element *rl, + s64 offs, s32 to_write, const char *outbuf) +{ + s32 count; + s32 put, xput; + s64 xpos; + BOOL first; + const char *xoutbuf; + const runlist_element *xrl; + + put = 0; + xrl = rl; + xoutbuf = outbuf; + first = TRUE; + do { + count = xrl->length << vol->cluster_size_bits; + xpos = xrl->lcn << vol->cluster_size_bits; + if (first) { + count -= offs; + xpos += offs; + } + if ((to_write - put) < count) + count = to_write - put; + xput = ntfs_pwrite(vol->dev, xpos, count, xoutbuf); + if (xput == count) { + put += count; + xpos += count; + xoutbuf += count; + xrl++; + } + first = FALSE; + } while ((xput == count) && (put < to_write)); + return (put); +} + + +/* + * Compress and write a set of blocks + * + * returns the size actually written (rounded to a full cluster) + * or 0 if all zeroes (nothing is written) + * or -1 if could not compress (nothing is written) + * or -2 if there were an irrecoverable error (errno set) + */ + +static s32 ntfs_comp_set(ntfs_attr *na, runlist_element *rl, + s64 offs, u32 insz, const char *inbuf) +{ + ntfs_volume *vol; + char *outbuf; + char *pbuf; + u32 compsz; + s32 written; + s32 rounded; + unsigned int clsz; + u32 p; + unsigned int sz; + unsigned int bsz; + BOOL fail; + BOOL allzeroes; + /* a single compressed zero */ + static char onezero[] = { 0x01, 0xb0, 0x00, 0x00 } ; + /* a couple of compressed zeroes */ + static char twozeroes[] = { 0x02, 0xb0, 0x00, 0x00, 0x00 } ; + /* more compressed zeroes, to be followed by some count */ + static char morezeroes[] = { 0x03, 0xb0, 0x02, 0x00 } ; + + vol = na->ni->vol; + written = -1; /* default return */ + clsz = 1 << vol->cluster_size_bits; + /* may need 2 extra bytes per block and 2 more bytes */ + outbuf = (char*)ntfs_malloc(na->compression_block_size + + 2*(na->compression_block_size/NTFS_SB_SIZE) + + 2); + if (outbuf) { + fail = FALSE; + compsz = 0; + allzeroes = TRUE; + for (p=0; (p<insz) && !fail; p+=NTFS_SB_SIZE) { + if ((p + NTFS_SB_SIZE) < insz) + bsz = NTFS_SB_SIZE; + else + bsz = insz - p; + pbuf = &outbuf[compsz]; + sz = ntfs_compress_block(&inbuf[p],bsz,pbuf); + /* fail if all the clusters (or more) are needed */ + if (!sz || ((compsz + sz + clsz + 2) + > na->compression_block_size)) + fail = TRUE; + else { + if (allzeroes) { + /* check whether this is all zeroes */ + switch (sz) { + case 4 : + allzeroes = !memcmp( + pbuf,onezero,4); + break; + case 5 : + allzeroes = !memcmp( + pbuf,twozeroes,5); + break; + case 6 : + allzeroes = !memcmp( + pbuf,morezeroes,4); + break; + default : + allzeroes = FALSE; + break; + } + } + compsz += sz; + } + } + if (!fail && !allzeroes) { + /* add a couple of null bytes, space has been checked */ + outbuf[compsz++] = 0; + outbuf[compsz++] = 0; + /* write a full cluster, to avoid partial reading */ + rounded = ((compsz - 1) | (clsz - 1)) + 1; + written = write_clusters(vol, rl, offs, rounded, outbuf); + if (written != rounded) { + /* + * TODO : previously written text has been + * spoilt, should return a specific error + */ + ntfs_log_error("error writing compressed data\n"); + errno = EIO; + written = -2; + } + } else + if (!fail) + written = 0; + free(outbuf); + } + return (written); +} + +/* + * Check the validity of a compressed runlist + * The check starts at the beginning of current run and ends + * at the end of runlist + * errno is set if the runlist is not valid + */ + +static BOOL valid_compressed_run(ntfs_attr *na, runlist_element *rl, + BOOL fullcheck, const char *text) +{ + runlist_element *xrl; + const char *err; + BOOL ok = TRUE; + + xrl = rl; + while (xrl->vcn & (na->compression_block_clusters - 1)) + xrl--; + err = (const char*)NULL; + while (xrl->length) { + if ((xrl->vcn + xrl->length) != xrl[1].vcn) + err = "Runs not adjacent"; + if (xrl->lcn == LCN_HOLE) { + if ((xrl->vcn + xrl->length) + & (na->compression_block_clusters - 1)) { + err = "Invalid hole"; + } + if (fullcheck && (xrl[1].lcn == LCN_HOLE)) { + err = "Adjacent holes"; + } + } + if (err) { + ntfs_log_error("%s at %s index %ld inode %lld\n", + err, text, (long)(xrl - na->rl), + (long long)na->ni->mft_no); + errno = EIO; + ok = FALSE; + err = (const char*)NULL; + } + xrl++; + } + return (ok); +} + +/* + * Free unneeded clusters after overwriting compressed data + * + * This generally requires one or two empty slots at the end of runlist, + * but we do not want to reallocate the runlist here because + * there are many pointers to it. + * So the empty slots have to be reserved beforehand + * + * Returns zero unless some error occurred (described by errno) + * + * +======= start of block =====+ + * 0 |A chunk may overflow | <-- rl usedcnt : A + B + * |A on previous block | then B + * |A | + * +-- end of allocated chunk --+ freelength : C + * |B | (incl overflow) + * +== end of compressed data ==+ + * |C | <-- freerl freecnt : C + D + * |C chunk may overflow | + * |C on next block | + * +-- end of allocated chunk --+ + * |D | + * |D chunk may overflow | + * 15 |D on next block | + * +======== end of block ======+ + * + */ + +static int ntfs_compress_overwr_free(ntfs_attr *na, runlist_element *rl, + s32 usedcnt, s32 freecnt, VCN *update_from) +{ + BOOL beginhole; + BOOL mergeholes; + s32 oldlength; + s32 freelength; + s64 freelcn; + s64 freevcn; + runlist_element *freerl; + ntfs_volume *vol; + s32 carry; + int res; + + vol = na->ni->vol; + res = 0; + freelcn = rl->lcn + usedcnt; + freevcn = rl->vcn + usedcnt; + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + /* can merge with hole before ? */ + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + /* truncate current run, carry to subsequent hole */ + carry = freelength; + oldlength = rl->length; + if (mergeholes) { + /* merging with a hole before */ + freerl = rl; + } else { + rl->length -= freelength; /* warning : can be zero */ + freerl = ++rl; + } + if (!mergeholes && (usedcnt || beginhole)) { + s32 freed; + runlist_element *frl; + runlist_element *erl; + int holes = 0; + BOOL threeparts; + + /* free the unneeded clusters from initial run, then freerl */ + threeparts = (freelength > freecnt); + freed = 0; + frl = freerl; + if (freelength) { + res = ntfs_cluster_free_basic(vol,freelcn, + (threeparts ? freecnt : freelength)); + if (!res) + freed += (threeparts ? freecnt : freelength); + if (!usedcnt) { + holes++; + freerl--; + freerl->length += (threeparts + ? freecnt : freelength); + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + } + } + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + if (!res) { + freed += frl->length; + frl->lcn = LCN_HOLE; + frl->length += carry; + carry = 0; + holes++; + } + } else { + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + if (!res) { + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + frl++; + } + na->compressed_size -= freed << vol->cluster_size_bits; + switch (holes) { + case 0 : + /* there are no hole, must insert one */ + /* space for hole has been prereserved */ + if (freerl->lcn == LCN_HOLE) { + if (threeparts) { + erl = freerl; + while (erl->length) + erl++; + do { + erl[2] = *erl; + } while (erl-- != freerl); + + freerl[1].length = freelength - freecnt; + freerl->length = freecnt; + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[2].lcn = LCN_HOLE; + freerl[2].vcn = freerl[1].vcn + + freerl[1].length; + freerl->vcn = freevcn; + } else { + freerl->vcn = freevcn; + freerl->length += freelength; + } + } else { + erl = freerl; + while (erl->length) + erl++; + if (threeparts) { + do { + erl[2] = *erl; + } while (erl-- != freerl); + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = oldlength - usedcnt - freecnt; + } else { + do { + erl[1] = *erl; + } while (erl-- != freerl); + } + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + break; + case 1 : + /* there is a single hole, may have to merge */ + freerl->vcn = freevcn; + freerl->length = freecnt; + if (freerl[1].lcn == LCN_HOLE) { + freerl->length += freerl[1].length; + erl = freerl; + do { + erl++; + *erl = erl[1]; + } while (erl->length); + } + break; + default : + /* there were several holes, must merge them */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + if (freerl[holes].lcn == LCN_HOLE) { + freerl->length += freerl[holes].length; + holes++; + } + erl = freerl; + do { + erl++; + *erl = erl[holes - 1]; + } while (erl->length); + break; + } + } else { + s32 freed; + runlist_element *frl; + runlist_element *xrl; + + freed = 0; + frl = freerl--; + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + freerl->length += frl->length; + freed += frl->length; + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + frl++; + } else { + freerl->length += freecnt - freed; + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + /* remove unneded runlist entries */ + xrl = freerl; + /* group with next run if also a hole */ + if (frl->length && (frl->lcn == LCN_HOLE)) { + xrl->length += frl->length; + frl++; + } + while (frl->length) { + *++xrl = *frl++; + } + *++xrl = *frl; /* terminator */ + na->compressed_size -= freed << vol->cluster_size_bits; + } + return (res); +} + + +/* + * Free unneeded clusters after compression + * + * This generally requires one or two empty slots at the end of runlist, + * but we do not want to reallocate the runlist here because + * there are many pointers to it. + * So the empty slots have to be reserved beforehand + * + * Returns zero unless some error occurred (described by errno) + */ + +static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, + s64 used, s64 reserved, BOOL appending, + VCN *update_from) +{ + s32 freecnt; + s32 usedcnt; + int res; + s64 freelcn; + s64 freevcn; + s32 freelength; + BOOL mergeholes; + BOOL beginhole; + ntfs_volume *vol; + runlist_element *freerl; + + res = -1; /* default return */ + vol = na->ni->vol; + freecnt = (reserved - used) >> vol->cluster_size_bits; + usedcnt = (reserved >> vol->cluster_size_bits) - freecnt; + if (rl->vcn < *update_from) + *update_from = rl->vcn; + /* skip entries fully used, if any */ + while (rl->length && (rl->length < usedcnt)) { + usedcnt -= rl->length; /* must be > 0 */ + rl++; + } + if (rl->length) { + /* + * Splitting the current allocation block requires + * an extra runlist element to create the hole. + * The required entry has been prereserved when + * mapping the runlist. + */ + /* get the free part in initial run */ + freelcn = rl->lcn + usedcnt; + freevcn = rl->vcn + usedcnt; + /* new count of allocated clusters */ + if (!((freevcn + freecnt) + & (na->compression_block_clusters - 1))) { + if (!appending) + res = ntfs_compress_overwr_free(na,rl, + usedcnt,freecnt,update_from); + else { + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + if (mergeholes) { + s32 carry; + + /* shorten the runs which have free space */ + carry = freecnt; + freerl = rl; + while (freerl->length < carry) { + carry -= freerl->length; + freerl++; + } + freerl->length = carry; + freerl = rl; + } else { + rl->length = usedcnt; /* can be zero ? */ + freerl = ++rl; + } + if ((freelength > 0) + && !mergeholes + && (usedcnt || beginhole)) { + /* + * move the unused part to the end. Doing so, + * the vcn will be out of order. This does + * not harm, the vcn are meaningless now, and + * only the lcn are meaningful for freeing. + */ + /* locate current end */ + while (rl->length) + rl++; + /* new terminator relocated */ + rl[1].vcn = rl->vcn; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + /* hole, currently allocated */ + rl->vcn = freevcn; + rl->lcn = freelcn; + rl->length = freelength; + } else { + /* why is this different from the begin hole case ? */ + if ((freelength > 0) + && !mergeholes + && !usedcnt) { + freerl--; + freerl->length = freelength; + if (freerl->vcn < *update_from) + *update_from + = freerl->vcn; + } + } + /* free the hole */ + res = ntfs_cluster_free_from_rl(vol,freerl); + if (!res) { + na->compressed_size -= freecnt + << vol->cluster_size_bits; + if (mergeholes) { + /* merge with adjacent hole */ + freerl--; + freerl->length += freecnt; + } else { + if (beginhole) + freerl--; + /* mark hole as free */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + /* and set up the new end */ + freerl[1].lcn = LCN_ENOENT; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = 0; + } + } + } else { + ntfs_log_error("Bad end of a compression block set\n"); + errno = EIO; + } + } else { + ntfs_log_error("No cluster to free after compression\n"); + errno = EIO; + } + NAttrSetRunlistDirty(na); + return (res); +} + +/* + * Read existing data, decompress and append buffer + * Do nothing if something fails + */ + +static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, + s64 offs, u32 compsz, s32 pos, BOOL appending, + char *outbuf, s64 to_write, const void *b) +{ + int fail = 1; + char *compbuf; + u32 decompsz; + u32 got; + + if (compsz == na->compression_block_size) { + /* if the full block was requested, it was a hole */ + memset(outbuf,0,compsz); + memcpy(&outbuf[pos],b,to_write); + fail = 0; + } else { + compbuf = (char*)ntfs_malloc(compsz); + if (compbuf) { + /* must align to full block for decompression */ + if (appending) + decompsz = ((pos - 1) | (NTFS_SB_SIZE - 1)) + 1; + else + decompsz = na->compression_block_size; + got = read_clusters(na->ni->vol, rl, offs, + compsz, compbuf); + if ((got == compsz) + && !ntfs_decompress((u8*)outbuf,decompsz, + (u8*)compbuf,compsz)) { + memcpy(&outbuf[pos],b,to_write); + fail = 0; + } + free(compbuf); + } + } + return (fail); +} + +/* + * Flush a full compression block + * + * returns the size actually written (rounded to a full cluster) + * or 0 if could not compress (and written uncompressed) + * or -1 if there were an irrecoverable error (errno set) + */ + +static s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, + const char *outbuf, s32 count, BOOL compress, + BOOL appending, VCN *update_from) +{ + s32 rounded; + s32 written; + int clsz; + + if (compress) { + written = ntfs_comp_set(na, rl, offs, count, outbuf); + if (written == -1) + compress = FALSE; + if ((written >= 0) + && ntfs_compress_free(na,rl,offs + written, + offs + na->compression_block_size, appending, + update_from)) + written = -1; + } else + written = 0; + if (!compress) { + clsz = 1 << na->ni->vol->cluster_size_bits; + rounded = ((count - 1) | (clsz - 1)) + 1; + written = write_clusters(na->ni->vol, rl, + offs, rounded, outbuf); + if (written != rounded) + written = -1; + } + return (written); +} + +/* + * Write some data to be compressed. + * Compression only occurs when a few clusters (usually 16) are + * full. When this occurs an extra runlist slot may be needed, so + * it has to be reserved beforehand. + * + * Returns the size of uncompressed data written, + * or negative if an error occurred. + * When the returned size is less than requested, new clusters have + * to be allocated before the function is called again. + */ + +s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, + s64 offs, s64 to_write, s64 rounded, + const void *b, int compressed_part, + VCN *update_from) +{ + ntfs_volume *vol; + runlist_element *brl; /* entry containing the beginning of block */ + int compression_length; + s64 written; + s64 to_read; + s64 to_flush; + s64 roffs; + s64 got; + s64 start_vcn; + s64 nextblock; + s64 endwrite; + u32 compsz; + char *inbuf; + char *outbuf; + BOOL fail; + BOOL done; + BOOL compress; + BOOL appending; + + if (!valid_compressed_run(na,wrl,FALSE,"begin compressed write")) { + return (-1); + } + if ((*update_from < 0) + || (compressed_part < 0) + || (compressed_part > (int)na->compression_block_clusters)) { + ntfs_log_error("Bad update vcn or compressed_part %d for compressed write\n", + compressed_part); + errno = EIO; + return (-1); + } + /* make sure there are two unused entries in runlist */ + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed write\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; + written = -1; /* default return */ + vol = na->ni->vol; + compression_length = na->compression_block_clusters; + compress = FALSE; + done = FALSE; + /* + * Cannot accept writing beyond the current compression set + * because when compression occurs, clusters are freed + * and have to be reallocated. + * (cannot happen with standard fuse 4K buffers) + * Caller has to avoid this situation, or face consequences. + */ + nextblock = ((offs + (wrl->vcn << vol->cluster_size_bits)) + | (na->compression_block_size - 1)) + 1; + /* determine whether we are appending to file */ + endwrite = offs + to_write + (wrl->vcn << vol->cluster_size_bits); + appending = endwrite >= na->initialized_size; + if (endwrite >= nextblock) { + /* it is time to compress */ + compress = TRUE; + /* only process what we can */ + to_write = rounded = nextblock + - (offs + (wrl->vcn << vol->cluster_size_bits)); + } + start_vcn = 0; + fail = FALSE; + brl = wrl; + roffs = 0; + /* + * If we are about to compress or we need to decompress + * existing data, we have to process a full set of blocks. + * So relocate the parameters to the beginning of allocation + * containing the first byte of the set of blocks. + */ + if (compress || compressed_part) { + /* find the beginning of block */ + start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) + & -compression_length; + if (start_vcn < *update_from) + *update_from = start_vcn; + while (brl->vcn && (brl->vcn > start_vcn)) { + /* jumping back a hole means big trouble */ + if (brl->lcn == (LCN)LCN_HOLE) { + ntfs_log_error("jump back over a hole when appending\n"); + fail = TRUE; + errno = EIO; + } + brl--; + offs += brl->length << vol->cluster_size_bits; + } + roffs = (start_vcn - brl->vcn) << vol->cluster_size_bits; + } + if (compressed_part && !fail) { + /* + * The set of compression blocks contains compressed data + * (we are reopening an existing file to append to it) + * Decompress the data and append + */ + compsz = (s32)compressed_part << vol->cluster_size_bits; + outbuf = (char*)ntfs_malloc(na->compression_block_size); + if (outbuf) { + if (appending) { + to_read = offs - roffs; + to_flush = to_read + to_write; + } else { + to_read = na->data_size + - (brl->vcn << vol->cluster_size_bits); + if (to_read > na->compression_block_size) + to_read = na->compression_block_size; + to_flush = to_read; + } + if (!ntfs_read_append(na, brl, roffs, compsz, + (s32)(offs - roffs), appending, + outbuf, to_write, b)) { + written = ntfs_flush(na, brl, roffs, + outbuf, to_flush, compress, appending, + update_from); + if (written >= 0) { + written = to_write; + done = TRUE; + } + } + free(outbuf); + } + } else { + if (compress && !fail) { + /* + * we are filling up a block, read the full set + * of blocks and compress it + */ + inbuf = (char*)ntfs_malloc(na->compression_block_size); + if (inbuf) { + to_read = offs - roffs; + if (to_read) + got = read_clusters(vol, brl, roffs, + to_read, inbuf); + else + got = 0; + if (got == to_read) { + memcpy(&inbuf[to_read],b,to_write); + written = ntfs_comp_set(na, brl, roffs, + to_read + to_write, inbuf); + /* + * if compression was not successful, + * only write the part which was requested + */ + if ((written >= 0) + /* free the unused clusters */ + && !ntfs_compress_free(na,brl, + written + roffs, + na->compression_block_size + + roffs, + appending, update_from)) { + done = TRUE; + written = to_write; + } + } + free(inbuf); + } + } + if (!done) { + /* + * if the compression block is not full, or + * if compression failed for whatever reason, + * write uncompressed + */ + /* check we are not overflowing current allocation */ + if ((wpos + rounded) + > ((wrl->lcn + wrl->length) + << vol->cluster_size_bits)) { + ntfs_log_error("writing on unallocated clusters\n"); + errno = EIO; + } else { + written = ntfs_pwrite(vol->dev, wpos, + rounded, b); + if (written == rounded) + written = to_write; + } + } + } + if ((written >= 0) + && !valid_compressed_run(na,wrl,TRUE,"end compressed write")) + written = -1; + return (written); +} + +/* + * Close a file written compressed. + * This compresses the last partial compression block of the file. + * Two empty runlist slots have to be reserved beforehand. + * + * Returns zero if closing is successful. + */ + +int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs, + VCN *update_from) +{ + ntfs_volume *vol; + runlist_element *brl; /* entry containing the beginning of block */ + int compression_length; + s64 written; + s64 to_read; + s64 roffs; + s64 got; + s64 start_vcn; + char *inbuf; + BOOL fail; + BOOL done; + + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed close\n"); + errno = EIO; + return (-1); + } + if (*update_from < 0) { + ntfs_log_error("Bad update vcn for compressed close\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; + vol = na->ni->vol; + compression_length = na->compression_block_clusters; + done = FALSE; + /* + * There generally is an uncompressed block at end of file, + * read the full block and compress it + */ + inbuf = (char*)ntfs_malloc(na->compression_block_size); + if (inbuf) { + start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) + & -compression_length; + if (start_vcn < *update_from) + *update_from = start_vcn; + to_read = offs + ((wrl->vcn - start_vcn) + << vol->cluster_size_bits); + brl = wrl; + fail = FALSE; + while (brl->vcn && (brl->vcn > start_vcn)) { + if (brl->lcn == (LCN)LCN_HOLE) { + ntfs_log_error("jump back over a hole when closing\n"); + fail = TRUE; + errno = EIO; + } + brl--; + } + if (!fail) { + /* roffs can be an offset from another uncomp block */ + roffs = (start_vcn - brl->vcn) + << vol->cluster_size_bits; + if (to_read) { + got = read_clusters(vol, brl, roffs, to_read, + inbuf); + if (got == to_read) { + written = ntfs_comp_set(na, brl, roffs, + to_read, inbuf); + if ((written >= 0) + /* free the unused clusters */ + && !ntfs_compress_free(na,brl, + written + roffs, + na->compression_block_size + roffs, + TRUE, update_from)) { + done = TRUE; + } else + /* if compression failed, leave uncompressed */ + if (written == -1) + done = TRUE; + } + } else + done = TRUE; + free(inbuf); + } + } + if (done && !valid_compressed_run(na,wrl,TRUE,"end compressed close")) + done = FALSE; + return (!done); +} diff --git a/libntfs-3g/debug.c b/libntfs-3g/debug.c new file mode 100755 index 0000000000000000000000000000000000000000..f19348330955ea7f47b2718e494c8b5340bfea13 --- /dev/null +++ b/libntfs-3g/debug.c @@ -0,0 +1,79 @@ +/** + * debug.c - Debugging output functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2004-2006 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "types.h" +#include "runlist.h" +#include "debug.h" +#include "logging.h" + +#ifdef DEBUG +/** + * ntfs_debug_runlist_dump - Dump a runlist. + * @rl: + * + * Description... + * + * Returns: + */ +void ntfs_debug_runlist_dump(const runlist_element *rl) +{ + int i = 0; + const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED", + "LCN_ENOENT ", "LCN_EINVAL ", + "LCN_unknown " }; + + ntfs_log_debug("NTFS-fs DEBUG: Dumping runlist (values in hex):\n"); + if (!rl) { + ntfs_log_debug("Run list not present.\n"); + return; + } + ntfs_log_debug("VCN LCN Run length\n"); + do { + LCN lcn = (rl + i)->lcn; + + if (lcn < (LCN)0) { + int idx = -lcn - 1; + + if (idx > -LCN_EINVAL - 1) + idx = 4; + ntfs_log_debug("%-16lld %s %-16lld%s\n", + (long long)rl[i].vcn, lcn_str[idx], + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); + } else + ntfs_log_debug("%-16lld %-16lld %-16lld%s\n", + (long long)rl[i].vcn, (long long)rl[i].lcn, + (long long)rl[i].length, + rl[i].length ? "" : " (runlist end)"); + } while (rl[i++].length); +} + +#endif + diff --git a/libntfs-3g/device.c b/libntfs-3g/device.c new file mode 100755 index 0000000000000000000000000000000000000000..a5c32f4227caaeac5ac62d83fb94a48f254c4f81 --- /dev/null +++ b/libntfs-3g/device.c @@ -0,0 +1,932 @@ +/** + * device.c - Low level device io functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004-2013 Anton Altaparmakov + * Copyright (c) 2004-2006 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * Copyright (c) 2008-2013 Tuxera Inc. + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif +#ifdef HAVE_SYS_MOUNT_H +#include <sys/mount.h> +#endif +#ifdef HAVE_SYS_DISK_H +#include <sys/disk.h> +#endif +#ifdef HAVE_LINUX_FD_H +#include <linux/fd.h> +#endif +#ifdef HAVE_LINUX_HDREG_H +#include <linux/hdreg.h> +#endif +#ifdef ENABLE_HD +#include <hd.h> +#endif + +#include "types.h" +#include "mst.h" +#include "debug.h" +#include "device.h" +#include "logging.h" +#include "misc.h" + +#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE) +#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */ +#endif +#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64) +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */ +#endif +#if defined(linux) && !defined(HDIO_GETGEO) +#define HDIO_GETGEO 0x0301 /* Get device geometry. */ +#endif +#if defined(linux) && defined(_IO) && !defined(BLKSSZGET) +# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */ +#endif +#if defined(linux) && defined(_IO) && !defined(BLKBSZSET) +# define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */ +#endif + +/** + * ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it + * @name: name of the device (must be present) + * @state: initial device state (usually zero) + * @dops: ntfs device operations to use with the device (must be present) + * @priv_data: pointer to private data (optional) + * + * Allocate an ntfs device structure and pre-initialize it with the user- + * specified device operations @dops, device state @state, device name @name, + * and optional private data @priv_data. + * + * Note, @name is copied and can hence be freed after this functions returns. + * + * On success return a pointer to the allocated ntfs device structure and on + * error return NULL with errno set to the error code returned by ntfs_malloc(). + */ +struct ntfs_device *ntfs_device_alloc(const char *name, const long state, + struct ntfs_device_operations *dops, void *priv_data) +{ + struct ntfs_device *dev; + + if (!name) { + errno = EINVAL; + return NULL; + } + + dev = ntfs_malloc(sizeof(struct ntfs_device)); + if (dev) { + if (!(dev->d_name = strdup(name))) { + int eo = errno; + free(dev); + errno = eo; + return NULL; + } + dev->d_ops = dops; + dev->d_state = state; + dev->d_private = priv_data; + dev->d_heads = -1; + dev->d_sectors_per_track = -1; + } + return dev; +} + +/** + * ntfs_device_free - free an ntfs device structure + * @dev: ntfs device structure to free + * + * Free the ntfs device structure @dev. + * + * Return 0 on success or -1 on error with errno set to the error code. The + * following error codes are defined: + * EINVAL Invalid pointer @dev. + * EBUSY Device is still open. Close it before freeing it! + */ +int ntfs_device_free(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } + if (NDevOpen(dev)) { + errno = EBUSY; + return -1; + } + free(dev->d_name); + free(dev); + return 0; +} + +/* + * Sync the device + * + * returns zero if successful. + */ + +int ntfs_device_sync(struct ntfs_device *dev) +{ + int ret; + struct ntfs_device_operations *dops; + + if (NDevDirty(dev)) { + dops = dev->d_ops; + ret = dops->sync(dev); + } else + ret = 0; + return ret; +} + +/** + * ntfs_pread - positioned read from disk + * @dev: device to read from + * @pos: position in device to read from + * @count: number of bytes to read + * @b: output data buffer + * + * This function will read @count bytes from device @dev at position @pos into + * the data buffer @b. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that we have either reached end of file or + * encountered an error during the read so that the read is partial. 0 means + * end of file or nothing to read (@count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of either seek, read, or set to EINVAL in case of + * invalid arguments. + */ +s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b) +{ + s64 br, total; + struct ntfs_device_operations *dops; + + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); + + if (!b || count < 0 || pos < 0) { + errno = EINVAL; + return -1; + } + if (!count) + return 0; + + dops = dev->d_ops; + + for (total = 0; count; count -= br, total += br) { + br = dops->pread(dev, (char*)b + total, count, pos + total); + /* If everything ok, continue. */ + if (br > 0) + continue; + /* If EOF or error return number of bytes read. */ + if (!br || total) + return total; + /* Nothing read and error, return error status. */ + return br; + } + /* Finally, return the number of bytes read. */ + return total; +} + +/** + * ntfs_pwrite - positioned write to disk + * @dev: device to write to + * @pos: position in file descriptor to write to + * @count: number of bytes to write + * @b: data buffer to write to disk + * + * This function will write @count bytes from data buffer @b to the device @dev + * at position @pos. + * + * On success, return the number of successfully written bytes. If this number + * is lower than @count this means that the write has been interrupted in + * flight or that an error was encountered during the write so that the write + * is partial. 0 means nothing was written (also return 0 when @count is 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of either seek, write, or set + * to EINVAL in case of invalid arguments. + */ +s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const void *b) +{ + s64 written, total, ret = -1; + struct ntfs_device_operations *dops; + + ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); + + if (!b || count < 0 || pos < 0) { + errno = EINVAL; + goto out; + } + if (!count) + return 0; + if (NDevReadOnly(dev)) { + errno = EROFS; + goto out; + } + + dops = dev->d_ops; + + NDevSetDirty(dev); + for (total = 0; count; count -= written, total += written) { + written = dops->pwrite(dev, (const char*)b + total, count, + pos + total); + /* If everything ok, continue. */ + if (written > 0) + continue; + /* + * If nothing written or error return number of bytes written. + */ + if (!written || total) + break; + /* Nothing written and error, return error status. */ + total = written; + break; + } + if (NDevSync(dev) && total && dops->sync(dev)) { + total--; /* on sync error, return partially written */ + } + ret = total; +out: + return ret; +} + +/** + * ntfs_mst_pread - multi sector transfer (mst) positioned read + * @dev: device to read from + * @pos: position in file descriptor to read from + * @count: number of blocks to read + * @bksize: size of each block that needs mst deprotecting + * @b: output data buffer + * + * Multi sector transfer (mst) positioned read. This function will read @count + * blocks of size @bksize bytes each from device @dev at position @pos into the + * the data buffer @b. + * + * On success, return the number of successfully read blocks. If this number is + * lower than @count this means that we have reached end of file, that the read + * was interrupted, or that an error was encountered during the read so that + * the read is partial. 0 means end of file or nothing was read (also return 0 + * when @count or @bksize are 0). + * + * On error and nothing was read, return -1 with errno set appropriately to the + * return code of either seek, read, or set to EINVAL in case of invalid + * arguments. + * + * NOTE: If an incomplete multi sector transfer has been detected the magic + * will have been changed to magic_BAAD but no error will be returned. Thus it + * is possible that we return count blocks as being read but that any number + * (between zero and count!) of these blocks is actually subject to a multi + * sector transfer error. This should be detected by the caller by checking for + * the magic being "BAAD". + */ +s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b) +{ + s64 br, i; + + if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) { + errno = EINVAL; + return -1; + } + /* Do the read. */ + br = ntfs_pread(dev, pos, count * bksize, b); + if (br < 0) + return br; + /* + * Apply fixups to successfully read data, disregarding any errors + * returned from the MST fixup function. This is because we want to + * fixup everything possible and we rely on the fact that the "BAAD" + * magic will be detected later on. + */ + count = br / bksize; + for (i = 0; i < count; ++i) + ntfs_mst_post_read_fixup((NTFS_RECORD*) + ((u8*)b + i * bksize), bksize); + /* Finally, return the number of complete blocks read. */ + return count; +} + +/** + * ntfs_mst_pwrite - multi sector transfer (mst) positioned write + * @dev: device to write to + * @pos: position in file descriptor to write to + * @count: number of blocks to write + * @bksize: size of each block that needs mst protecting + * @b: data buffer to write to disk + * + * Multi sector transfer (mst) positioned write. This function will write + * @count blocks of size @bksize bytes each from data buffer @b to the device + * @dev at position @pos. + * + * On success, return the number of successfully written blocks. If this number + * is lower than @count this means that the write has been interrupted or that + * an error was encountered during the write so that the write is partial. 0 + * means nothing was written (also return 0 when @count or @bksize are 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of either seek, write, or set + * to EINVAL in case of invalid arguments. + * + * NOTE: We mst protect the data, write it, then mst deprotect it using a quick + * deprotect algorithm (no checking). This saves us from making a copy before + * the write and at the same time causes the usn to be incremented in the + * buffer. This conceptually fits in better with the idea that cached data is + * always deprotected and protection is performed when the data is actually + * going to hit the disk and the cache is immediately deprotected again + * simulating an mst read on the written data. This way cache coherency is + * achieved. + */ +s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, + const u32 bksize, void *b) +{ + s64 written, i; + + if (count < 0 || bksize % NTFS_BLOCK_SIZE) { + errno = EINVAL; + return -1; + } + if (!count) + return 0; + /* Prepare data for writing. */ + for (i = 0; i < count; ++i) { + int err; + + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) + ((u8*)b + i * bksize), bksize); + if (err < 0) { + /* Abort write at this position. */ + if (!i) + return err; + count = i; + break; + } + } + /* Write the prepared data. */ + written = ntfs_pwrite(dev, pos, count * bksize, b); + /* Quickly deprotect the data again. */ + for (i = 0; i < count; ++i) + ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize)); + if (written <= 0) + return written; + /* Finally, return the number of complete blocks written. */ + return written / bksize; +} + +/** + * ntfs_cluster_read - read ntfs clusters + * @vol: volume to read from + * @lcn: starting logical cluster number + * @count: number of clusters to read + * @b: output data buffer + * + * Read @count ntfs clusters starting at logical cluster number @lcn from + * volume @vol into buffer @b. Return number of clusters read or -1 on error, + * with errno set to the error code. + */ +s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count, + void *b) +{ + s64 br; + + if (!vol || lcn < 0 || count < 0) { + errno = EINVAL; + return -1; + } + if (vol->nr_clusters < lcn + count) { + errno = ESPIPE; + ntfs_log_perror("Trying to read outside of volume " + "(%lld < %lld)", (long long)vol->nr_clusters, + (long long)lcn + count); + return -1; + } + br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits, + count << vol->cluster_size_bits, b); + if (br < 0) { + ntfs_log_perror("Error reading cluster(s)"); + return br; + } + return br >> vol->cluster_size_bits; +} + +/** + * ntfs_cluster_write - write ntfs clusters + * @vol: volume to write to + * @lcn: starting logical cluster number + * @count: number of clusters to write + * @b: data buffer to write to disk + * + * Write @count ntfs clusters starting at logical cluster number @lcn from + * buffer @b to volume @vol. Return the number of clusters written or -1 on + * error, with errno set to the error code. + */ +s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, + const s64 count, const void *b) +{ + s64 bw; + + if (!vol || lcn < 0 || count < 0) { + errno = EINVAL; + return -1; + } + if (vol->nr_clusters < lcn + count) { + errno = ESPIPE; + ntfs_log_perror("Trying to write outside of volume " + "(%lld < %lld)", (long long)vol->nr_clusters, + (long long)lcn + count); + return -1; + } + if (!NVolReadOnly(vol)) + bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits, + count << vol->cluster_size_bits, b); + else + bw = count << vol->cluster_size_bits; + if (bw < 0) { + ntfs_log_perror("Error writing cluster(s)"); + return bw; + } + return bw >> vol->cluster_size_bits; +} + +/** + * ntfs_device_offset_valid - test if a device offset is valid + * @dev: open device + * @ofs: offset to test for validity + * + * Test if the offset @ofs is an existing location on the device described + * by the open device structure @dev. + * + * Return 0 if it is valid and -1 if it is not valid. + */ +static int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs) +{ + char ch; + + if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 && + dev->d_ops->read(dev, &ch, 1) == 1) + return 0; + return -1; +} + +/** + * ntfs_device_size_get - return the size of a device in blocks + * @dev: open device + * @block_size: block size in bytes in which to return the result + * + * Return the number of @block_size sized blocks in the device described by the + * open device @dev. + * + * Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o. + * + * On error return -1 with errno set to the error code. + */ +s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size) +{ + s64 high, low; + + if (!dev || block_size <= 0 || (block_size - 1) & block_size) { + errno = EINVAL; + return -1; + } +#ifdef BLKGETSIZE64 + { u64 size; + + if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n", + (unsigned long long)size, + (unsigned long long)size); + return (s64)size / block_size; + } + } +#endif +#ifdef BLKGETSIZE + { unsigned long size; + + if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n", + size, size); + return (s64)size * 512 / block_size; + } + } +#endif +#ifdef FDGETPRM + { struct floppy_struct this_floppy; + + if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) { + ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n", + (unsigned long)this_floppy.size, + (unsigned long)this_floppy.size); + return (s64)this_floppy.size * 512 / block_size; + } + } +#endif +#ifdef DIOCGMEDIASIZE + { + /* FreeBSD */ + off_t size; + + if (dev->d_ops->ioctl(dev, DIOCGMEDIASIZE, &size) >= 0) { + ntfs_log_debug("DIOCGMEDIASIZE nr bytes = %llu (0x%llx)\n", + (unsigned long long)size, + (unsigned long long)size); + return (s64)size / block_size; + } + } +#endif +#ifdef DKIOCGETBLOCKCOUNT + { + /* Mac OS X */ + uint64_t blocks; + int sector_size; + + sector_size = ntfs_device_sector_size_get(dev); + if (sector_size >= 0 && dev->d_ops->ioctl(dev, + DKIOCGETBLOCKCOUNT, &blocks) >= 0) + { + ntfs_log_debug("DKIOCGETBLOCKCOUNT nr blocks = %llu (0x%llx)\n", + (unsigned long long) blocks, + (unsigned long long) blocks); + return blocks * sector_size / block_size; + } + } +#endif + /* + * We couldn't figure it out by using a specialized ioctl, + * so do binary search to find the size of the device. + */ + low = 0LL; + for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1) + low = high; + while (low < high - 1LL) { + const s64 mid = (low + high) / 2; + + if (!ntfs_device_offset_valid(dev, mid)) + low = mid; + else + high = mid; + } + dev->d_ops->seek(dev, 0LL, SEEK_SET); + return (low + 1LL) / block_size; +} + +/** + * ntfs_device_partition_start_sector_get - get starting sector of a partition + * @dev: open device + * + * On success, return the starting sector of the partition @dev in the parent + * block device of @dev. On error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + */ +s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef HDIO_GETGEO + { struct hd_geometry geo; + + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n", + geo.start, geo.start); + return geo.start; + } + } +#else + errno = EOPNOTSUPP; +#endif + return -1; +} + +static int ntfs_device_get_geo(struct ntfs_device *dev) +{ + int err; + + if (!dev) { + errno = EINVAL; + return -1; + } + err = EOPNOTSUPP; +#ifdef ENABLE_HD + { + hd_data_t *hddata; + hd_t *hd, *devlist, *partlist = NULL; + str_list_t *names; + hd_res_t *res; + const int d_name_len = strlen(dev->d_name) + 1; + int done = 0; + + hddata = calloc(1, sizeof(*hddata)); + if (!hddata) { + err = ENOMEM; + goto skip_hd; + } + /* List all "disk" class devices on the system. */ + devlist = hd_list(hddata, hw_disk, 1, NULL); + if (!devlist) { + free(hddata); + err = ENOMEM; + goto skip_hd; + } + /* + * Loop over each disk device looking for the device with the + * same unix name as @dev. + */ + for (hd = devlist; hd; hd = hd->next) { + if (hd->unix_dev_name && !strncmp(dev->d_name, + hd->unix_dev_name, d_name_len)) + goto got_hd; + if (hd->unix_dev_name2 && !strncmp(dev->d_name, + hd->unix_dev_name2, d_name_len)) + goto got_hd; + for (names = hd->unix_dev_names; names; + names = names->next) { + if (names->str && !strncmp(dev->d_name, + names->str, d_name_len)) + goto got_hd; + } + } + /* + * Device was not a whole disk device. Unless it is a file it + * is likely to be a partition device. List all "partition" + * class devices on the system. + */ + partlist = hd_list(hddata, hw_partition, 1, NULL); + for (hd = partlist; hd; hd = hd->next) { + if (hd->unix_dev_name && !strncmp(dev->d_name, + hd->unix_dev_name, d_name_len)) + goto got_part_hd; + if (hd->unix_dev_name2 && !strncmp(dev->d_name, + hd->unix_dev_name2, d_name_len)) + goto got_part_hd; + for (names = hd->unix_dev_names; names; + names = names->next) { + if (names->str && !strncmp(dev->d_name, + names->str, d_name_len)) + goto got_part_hd; + } + } + /* Failed to find the device. Stop trying and clean up. */ + goto end_hd; +got_part_hd: + /* Get the whole block device the partition device is on. */ + hd = hd_get_device_by_idx(hddata, hd->attached_to); + if (!hd) + goto end_hd; +got_hd: + /* + * @hd is now the whole block device either being formatted or + * that the partition being formatted is on. + * + * Loop over each resource of the disk device looking for the + * BIOS legacy geometry obtained from EDD which is what Windows + * needs to boot. + */ + for (res = hd->res; res; res = res->next) { + /* geotype 3 is BIOS legacy. */ + if (res->any.type != res_disk_geo || + res->disk_geo.geotype != 3) + continue; + dev->d_heads = res->disk_geo.heads; + dev->d_sectors_per_track = res->disk_geo.sectors; + done = 1; + } +end_hd: + if (partlist) + hd_free_hd_list(partlist); + hd_free_hd_list(devlist); + hd_free_hd_data(hddata); + free(hddata); + if (done) { + ntfs_log_debug("EDD/BIOD legacy heads = %u, sectors " + "per track = %u\n", dev->d_heads, + dev->d_sectors_per_track); + return 0; + } + } +skip_hd: +#endif +#ifdef HDIO_GETGEO + { struct hd_geometry geo; + + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + dev->d_heads = geo.heads; + dev->d_sectors_per_track = geo.sectors; + ntfs_log_debug("HDIO_GETGEO heads = %u, sectors per " + "track = %u\n", dev->d_heads, + dev->d_sectors_per_track); + return 0; + } + err = errno; + } +#endif + errno = err; + return -1; +} + +/** + * ntfs_device_heads_get - get number of heads of device + * @dev: open device + * + * On success, return the number of heads on the device @dev. On error return + * -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + * ENOMEM Not enough memory to complete the request + */ +int ntfs_device_heads_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } + if (dev->d_heads == -1) { + if (ntfs_device_get_geo(dev) == -1) + return -1; + if (dev->d_heads == -1) { + errno = EINVAL; + return -1; + } + } + return dev->d_heads; +} + +/** + * ntfs_device_sectors_per_track_get - get number of sectors per track of device + * @dev: open device + * + * On success, return the number of sectors per track on the device @dev. On + * error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support HDIO_GETGEO ioctl + * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + * ENOMEM Not enough memory to complete the request + */ +int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } + if (dev->d_sectors_per_track == -1) { + if (ntfs_device_get_geo(dev) == -1) + return -1; + if (dev->d_sectors_per_track == -1) { + errno = EINVAL; + return -1; + } + } + return dev->d_sectors_per_track; +} + +/** + * ntfs_device_sector_size_get - get sector size of a device + * @dev: open device + * + * On success, return the sector size in bytes of the device @dev. + * On error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support BLKSSZGET ioctl + * ENOTTY @dev is a file or a device not supporting BLKSSZGET + */ +int ntfs_device_sector_size_get(struct ntfs_device *dev) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef BLKSSZGET + { + int sect_size = 0; + + if (!dev->d_ops->ioctl(dev, BLKSSZGET, §_size)) { + ntfs_log_debug("BLKSSZGET sector size = %d bytes\n", + sect_size); + return sect_size; + } + } +#elif defined(DIOCGSECTORSIZE) + { + /* FreeBSD */ + size_t sect_size = 0; + + if (!dev->d_ops->ioctl(dev, DIOCGSECTORSIZE, §_size)) { + ntfs_log_debug("DIOCGSECTORSIZE sector size = %d bytes\n", + (int) sect_size); + return sect_size; + } + } +#elif defined(DKIOCGETBLOCKSIZE) + { + /* Mac OS X */ + uint32_t sect_size = 0; + + if (!dev->d_ops->ioctl(dev, DKIOCGETBLOCKSIZE, §_size)) { + ntfs_log_debug("DKIOCGETBLOCKSIZE sector size = %d bytes\n", + (int) sect_size); + return sect_size; + } + } +#else + errno = EOPNOTSUPP; +#endif + return -1; +} + +/** + * ntfs_device_block_size_set - set block size of a device + * @dev: open device + * @block_size: block size to set @dev to + * + * On success, return 0. + * On error return -1 with errno set to the error code. + * + * The following error codes are defined: + * EINVAL Input parameter error + * EOPNOTSUPP System does not support BLKBSZSET ioctl + * ENOTTY @dev is a file or a device not supporting BLKBSZSET + */ +int ntfs_device_block_size_set(struct ntfs_device *dev, + int block_size __attribute__((unused))) +{ + if (!dev) { + errno = EINVAL; + return -1; + } +#ifdef BLKBSZSET + { + size_t s_block_size = block_size; + if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) { + ntfs_log_debug("Used BLKBSZSET to set block size to " + "%d bytes.\n", block_size); + return 0; + } + /* If not a block device, pretend it was successful. */ + if (!NDevBlock(dev)) + return 0; + } +#else + /* If not a block device, pretend it was successful. */ + if (!NDevBlock(dev)) + return 0; + errno = EOPNOTSUPP; +#endif + return -1; +} diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c new file mode 100755 index 0000000000000000000000000000000000000000..8633c7d9b13f550ac109654e79838c75cb0a5981 --- /dev/null +++ b/libntfs-3g/dir.c @@ -0,0 +1,2787 @@ +/** + * dir.c - Directory handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2005-2007 Yura Pakhuchiy + * Copyright (c) 2008-2014 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include <sys/sysmacros.h> +#endif + +#include "param.h" +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "ntfstime.h" +#include "lcnalloc.h" +#include "logging.h" +#include "cache.h" +#include "misc.h" +#include "security.h" +#include "reparse.h" +#include "object_id.h" + +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#endif + +/* + * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O" + * and "$Q" as global constants. + */ +ntfschar NTFS_INDEX_I30[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('I'), + const_cpu_to_le16('3'), const_cpu_to_le16('0'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_SII[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), + const_cpu_to_le16('I'), const_cpu_to_le16('I'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_SDH[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), + const_cpu_to_le16('D'), const_cpu_to_le16('H'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_O[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('O'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_Q[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('Q'), + const_cpu_to_le16('\0') }; +ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'), + const_cpu_to_le16('\0') }; + +#if CACHE_INODE_SIZE + +/* + * Pathname hashing + * + * Based on first char and second char (which may be '\0') + */ + +int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached) +{ + const char *path; + const unsigned char *name; + + path = (const char*)cached->variable; + if (!path) { + ntfs_log_error("Bad inode cache entry\n"); + return (-1); + } + name = (const unsigned char*)strrchr(path,'/'); + if (!name) + name = (const unsigned char*)path; + return (((name[0] << 1) + name[1] + strlen((const char*)name)) + % (2*CACHE_INODE_SIZE)); +} + +/* + * Pathname comparing for entering/fetching from cache + */ + +static int inode_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + return (!cached->variable + || strcmp(cached->variable, wanted->variable)); +} + +/* + * Pathname comparing for invalidating entries in cache + * + * A partial path is compared in order to invalidate all paths + * related to a renamed directory + * inode numbers are also checked, as deleting a long name may + * imply deleting a short name and conversely + * + * Only use associated with a CACHE_NOHASH flag + */ + +static int inode_cache_inv_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + int len; + BOOL different; + const struct CACHED_INODE *w; + const struct CACHED_INODE *c; + + w = (const struct CACHED_INODE*)wanted; + c = (const struct CACHED_INODE*)cached; + if (w->pathname) { + len = strlen(w->pathname); + different = !cached->variable + || ((w->inum != MREF(c->inum)) + && (strncmp(c->pathname, w->pathname, len) + || ((c->pathname[len] != '\0') + && (c->pathname[len] != '/')))); + } else + different = !c->pathname + || (w->inum != MREF(c->inum)); + return (different); +} + +#endif + +#if CACHE_LOOKUP_SIZE + +/* + * File name comparing for entering/fetching from lookup cache + */ + +static int lookup_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (c->namesize != w->namesize) + || memcmp(c->name, w->name, c->namesize)); +} + +/* + * Inode number comparing for invalidating lookup cache + * + * All entries with designated inode number are invalidated + * + * Only use associated with a CACHE_NOHASH flag + */ + +static int lookup_cache_inv_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (MREF(c->inum) != MREF(w->inum))); +} + +/* + * Lookup hashing + * + * Based on first, second and and last char + */ + +int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached) +{ + const unsigned char *name; + int count; + unsigned int val; + + name = (const unsigned char*)cached->variable; + count = cached->varsize; + if (!name || !count) { + ntfs_log_error("Bad lookup cache entry\n"); + return (-1); + } + val = (name[0] << 2) + (name[1] << 1) + name[count - 1] + count; + return (val % (2*CACHE_LOOKUP_SIZE)); +} + +#endif + +/** + * ntfs_inode_lookup_by_name - find an inode in a directory given its name + * @dir_ni: ntfs inode of the directory in which to search for the name + * @uname: Unicode name for which to search in the directory + * @uname_len: length of the name @uname in Unicode characters + * + * Look for an inode with name @uname in the directory with inode @dir_ni. + * ntfs_inode_lookup_by_name() walks the contents of the directory looking for + * the Unicode name. If the name is found in the directory, the corresponding + * inode number (>= 0) is returned as a mft reference in cpu format, i.e. it + * is a 64-bit number containing the sequence number. + * + * On error, return -1 with errno set to the error code. If the inode is is not + * found errno is ENOENT. + * + * Note, @uname_len does not include the (optional) terminating NULL character. + * + * Note, we look for a case sensitive match first but we also look for a case + * insensitive match at the same time. If we find a case insensitive match, we + * save that for the case that we don't find an exact match, where we return + * the mft reference of the case insensitive match. + * + * If the volume is mounted with the case sensitive flag set, then we only + * allow exact matches. + */ +u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, + const ntfschar *uname, const int uname_len) +{ + VCN vcn; + u64 mref = 0; + s64 br; + ntfs_volume *vol = dir_ni->vol; + ntfs_attr_search_ctx *ctx; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_ALLOCATION *ia; + IGNORE_CASE_BOOL case_sensitivity; + u8 *index_end; + ntfs_attr *ia_na; + int eo, rc; + u32 index_block_size; + u8 index_vcn_size_bits; + + ntfs_log_trace("Entering\n"); + + if (!dir_ni || !dir_ni->mrec || !uname || uname_len <= 0) { + errno = EINVAL; + return -1; + } + + ctx = ntfs_attr_get_search_ctx(dir_ni, NULL); + if (!ctx) + return -1; + + /* Find the index root attribute in the mft record. */ + if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, + 0, ctx)) { + ntfs_log_perror("Index root attribute missing in directory inode " + "%lld", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + case_sensitivity = (NVolCaseSensitive(vol) ? CASE_SENSITIVE : IGNORE_CASE); + /* Get to the index root value. */ + ir = (INDEX_ROOT*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + index_block_size = le32_to_cpu(ir->index_block_size); + if (index_block_size < NTFS_BLOCK_SIZE || + index_block_size & (index_block_size - 1)) { + ntfs_log_error("Index block size %u is invalid.\n", + (unsigned)index_block_size); + goto put_err_out; + } + index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ir->index + + le32_to_cpu(ir->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + /* Bounds checks. */ + if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + /* + * The last entry cannot contain a name. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) { + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_names_full_collate(uname, uname_len, + (ntfschar*)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); + /* + * If uname collates before the name of the current entry, there + * is definitely no such name in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* The names are not equal, continue the search. */ + if (rc) + continue; + /* + * Perfect match, this will never happen as the + * ntfs_are_names_equal() call will have gotten a match but we + * still treat it correctly. + */ + mref = le64_to_cpu(ie->indexed_file); + ntfs_attr_put_search_ctx(ctx); + return mref; + } + /* + * We have finished with this index without success. Check for the + * presence of a child node and if not present return error code + * ENOENT, unless we have got the mft reference of a matching name + * cached in mref in which case return mref. + */ + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) { + ntfs_attr_put_search_ctx(ctx); + if (mref) + return mref; + ntfs_log_debug("Entry not found - between root entries.\n"); + errno = ENOENT; + return -1; + } /* Child node present, descend into it. */ + + /* Open the index allocation attribute. */ + ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (!ia_na) { + ntfs_log_perror("Failed to open index allocation (inode %lld)", + (unsigned long long)dir_ni->mft_no); + goto put_err_out; + } + + /* Allocate a buffer for the current index block. */ + ia = ntfs_malloc(index_block_size); + if (!ia) { + ntfs_attr_close(ia_na); + goto put_err_out; + } + + /* Determine the size of a vcn in the directory index. */ + if (vol->cluster_size <= index_block_size) { + index_vcn_size_bits = vol->cluster_size_bits; + } else { + index_vcn_size_bits = NTFS_BLOCK_SIZE_BITS; + } + + /* Get the starting vcn of the index_block holding the child node. */ + vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); + +descend_into_child_node: + + /* Read the index block starting at vcn. */ + br = ntfs_attr_mst_pread(ia_na, vcn << index_vcn_size_bits, 1, + index_block_size, ia); + if (br != 1) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read vcn 0x%llx", + (unsigned long long)vcn); + goto close_err_out; + } + + if (sle64_to_cpu(ia->index_block_vcn) != vcn) { + ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " + "from expected VCN (0x%llx).\n", + (long long)sle64_to_cpu(ia->index_block_vcn), + (long long)vcn); + errno = EIO; + goto close_err_out; + } + if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx " + "has a size (%u) differing from the directory " + "specified size (%u).\n", (long long)vcn, + (unsigned long long)dir_ni->mft_no, + (unsigned) le32_to_cpu(ia->index.allocated_size) + 0x18, + (unsigned)index_block_size); + errno = EIO; + goto close_err_out; + } + index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); + if (index_end > (u8*)ia + index_block_size) { + ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " + "0x%llx exceeds maximum size.\n", + (long long)vcn, (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ia->index + + le32_to_cpu(ia->index.entries_offset)); + /* + * Iterate similar to above big loop but applied to index buffer, thus + * loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + /* Bounds check. */ + if ((u8*)ie < (u8*)ia || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in directory " + "inode %lld.\n", + (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + /* + * The last entry cannot contain a name. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) { + errno = EIO; + ntfs_log_error("Zero length index entry in inode %lld" + "\n", (unsigned long long)dir_ni->mft_no); + goto close_err_out; + } + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + rc = ntfs_names_full_collate(uname, uname_len, + (ntfschar*)&ie->key.file_name.file_name, + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); + /* + * If uname collates before the name of the current entry, there + * is definitely no such name in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + /* The names are not equal, continue the search. */ + if (rc) + continue; + mref = le64_to_cpu(ie->indexed_file); + free(ia); + ntfs_attr_close(ia_na); + ntfs_attr_put_search_ctx(ctx); + return mref; + } + /* + * We have finished with this index buffer without success. Check for + * the presence of a child node. + */ + if (ie->ie_flags & INDEX_ENTRY_NODE) { + if ((ia->index.ih_flags & NODE_MASK) == LEAF_NODE) { + ntfs_log_error("Index entry with child node found in a leaf " + "node in directory inode %lld.\n", + (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + /* Child node present, descend into it. */ + vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); + if (vcn >= 0) + goto descend_into_child_node; + ntfs_log_error("Negative child node vcn in directory inode " + "0x%llx.\n", (unsigned long long)dir_ni->mft_no); + errno = EIO; + goto close_err_out; + } + free(ia); + ntfs_attr_close(ia_na); + ntfs_attr_put_search_ctx(ctx); + /* + * No child node present, return error code ENOENT, unless we have got + * the mft reference of a matching name cached in mref in which case + * return mref. + */ + if (mref) + return mref; + ntfs_log_debug("Entry not found.\n"); + errno = ENOENT; + return -1; +put_err_out: + eo = EIO; + ntfs_log_debug("Corrupt directory. Aborting lookup.\n"); +eo_put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = eo; + return -1; +close_err_out: + eo = errno; + free(ia); + ntfs_attr_close(ia_na); + goto eo_put_err_out; +} + +/* + * Lookup a file in a directory from its UTF-8 name + * + * The name is first fetched from cache if one is defined + * + * Returns the inode number + * or -1 if not possible (errno tells why) + */ + +u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name) +{ + int uname_len; + ntfschar *uname = (ntfschar*)NULL; + u64 inum; + char *cached_name; + const char *const_name; + + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + const_name = cached_name; + } else { + cached_name = (char*)NULL; + const_name = name; + } + if (const_name) { +#if CACHE_LOOKUP_SIZE + + /* + * fetch inode from cache + */ + + if (dir_ni->vol->lookup_cache) { + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; + + item.name = const_name; + item.namesize = strlen(const_name) + 1; + item.parent = dir_ni->mft_no; + cached = (struct CACHED_LOOKUP*)ntfs_fetch_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) { + inum = cached->inum; + if (inum == (u64)-1) + errno = ENOENT; + } else { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(name, &uname); + if (uname_len >= 0) { + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + item.inum = inum; + /* enter into cache, even if not found */ + ntfs_enter_cache(dir_ni->vol->lookup_cache, + GENERIC(&item), + lookup_cache_compare); + free(uname); + } else + inum = (s64)-1; + } + } else +#endif + { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(cached_name, &uname); + if (uname_len >= 0) + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + else + inum = (s64)-1; + } + if (cached_name) + free(cached_name); + } else + inum = (s64)-1; + return (inum); +} + +/* + * Update a cache lookup record when a name has been defined + * + * The UTF-8 name is required + */ + +void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, u64 inum) +{ +#if CACHE_LOOKUP_SIZE + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; + char *cached_name; + + if (dir_ni->vol->lookup_cache) { + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + item.name = cached_name; + } else { + cached_name = (char*)NULL; + item.name = name; + } + if (item.name) { + item.namesize = strlen(item.name) + 1; + item.parent = dir_ni->mft_no; + item.inum = inum; + cached = (struct CACHED_LOOKUP*)ntfs_enter_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) + cached->inum = inum; + if (cached_name) + free(cached_name); + } + } +#endif +} + +/** + * ntfs_pathname_to_inode - Find the inode which represents the given pathname + * @vol: An ntfs volume obtained from ntfs_mount + * @parent: A directory inode to begin the search (may be NULL) + * @pathname: Pathname to be located + * + * Take an ASCII pathname and find the inode that represents it. The function + * splits the path and then descends the directory tree. If @parent is NULL, + * then the root directory '.' will be used as the base for the search. + * + * Return: inode Success, the pathname was valid + * NULL Error, the pathname was invalid, or some other error occurred + */ +ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, + const char *pathname) +{ + u64 inum; + int len, err = 0; + char *p, *q; + ntfs_inode *ni; + ntfs_inode *result = NULL; + ntfschar *unicode = NULL; + char *ascii = NULL; +#if CACHE_INODE_SIZE + struct CACHED_INODE item; + struct CACHED_INODE *cached; + char *fullname; +#endif + + if (!vol || !pathname) { + errno = EINVAL; + return NULL; + } + + ntfs_log_trace("path: '%s'\n", pathname); + + ascii = strdup(pathname); + if (!ascii) { + ntfs_log_error("Out of memory.\n"); + err = ENOMEM; + goto out; + } + + p = ascii; + /* Remove leading /'s. */ + while (p && *p && *p == PATH_SEP) + p++; +#if CACHE_INODE_SIZE + fullname = p; + if (p[0] && (p[strlen(p)-1] == PATH_SEP)) + ntfs_log_error("Unnormalized path %s\n",ascii); +#endif + if (parent) { + ni = parent; + } else { +#if CACHE_INODE_SIZE + /* + * fetch inode for full path from cache + */ + if (*fullname) { + item.pathname = fullname; + item.varsize = strlen(fullname) + 1; + cached = (struct CACHED_INODE*)ntfs_fetch_cache( + vol->xinode_cache, GENERIC(&item), + inode_cache_compare); + } else + cached = (struct CACHED_INODE*)NULL; + if (cached) { + /* + * return opened inode if found in cache + */ + inum = MREF(cached->inum); + ni = ntfs_inode_open(vol, inum); + if (!ni) { + ntfs_log_debug("Cannot open inode %llu: %s.\n", + (unsigned long long)inum, p); + err = EIO; + } + result = ni; + goto out; + } +#endif + ni = ntfs_inode_open(vol, FILE_root); + if (!ni) { + ntfs_log_debug("Couldn't open the inode of the root " + "directory.\n"); + err = EIO; + result = (ntfs_inode*)NULL; + goto out; + } + } + + while (p && *p) { + /* Find the end of the first token. */ + q = strchr(p, PATH_SEP); + if (q != NULL) { + *q = '\0'; + } +#if CACHE_INODE_SIZE + /* + * fetch inode for partial path from cache + */ + cached = (struct CACHED_INODE*)NULL; + if (!parent) { + item.pathname = fullname; + item.varsize = strlen(fullname) + 1; + cached = (struct CACHED_INODE*)ntfs_fetch_cache( + vol->xinode_cache, GENERIC(&item), + inode_cache_compare); + if (cached) { + inum = cached->inum; + } + } + /* + * if not in cache, translate, search, then + * insert into cache if found + */ + if (!cached) { + len = ntfs_mbstoucs(p, &unicode); + if (len < 0) { + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); + err = errno; + goto close; + } else if (len > NTFS_MAX_NAME_LEN) { + err = ENAMETOOLONG; + goto close; + } + inum = ntfs_inode_lookup_by_name(ni, unicode, len); + if (!parent && (inum != (u64) -1)) { + item.inum = inum; + ntfs_enter_cache(vol->xinode_cache, + GENERIC(&item), + inode_cache_compare); + } + } +#else + len = ntfs_mbstoucs(p, &unicode); + if (len < 0) { + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); + err = errno; + goto close; + } else if (len > NTFS_MAX_NAME_LEN) { + err = ENAMETOOLONG; + goto close; + } + inum = ntfs_inode_lookup_by_name(ni, unicode, len); +#endif + if (inum == (u64) -1) { + ntfs_log_debug("Couldn't find name '%s' in pathname " + "'%s'.\n", p, pathname); + err = ENOENT; + goto close; + } + + if (ni != parent) + if (ntfs_inode_close(ni)) { + err = errno; + goto out; + } + + inum = MREF(inum); + ni = ntfs_inode_open(vol, inum); + if (!ni) { + ntfs_log_debug("Cannot open inode %llu: %s.\n", + (unsigned long long)inum, p); + err = EIO; + goto close; + } + + free(unicode); + unicode = NULL; + + if (q) *q++ = PATH_SEP; /* JPA */ + p = q; + while (p && *p && *p == PATH_SEP) + p++; + } + + result = ni; + ni = NULL; +close: + if (ni && (ni != parent)) + if (ntfs_inode_close(ni) && !err) + err = errno; +out: + free(ascii); + free(unicode); + if (err) + errno = err; + return result; +} + +/* + * The little endian Unicode string ".." for ntfs_readdir(). + */ +static const ntfschar dotdot[3] = { const_cpu_to_le16('.'), + const_cpu_to_le16('.'), + const_cpu_to_le16('\0') }; + +/* + * union index_union - + * More helpers for ntfs_readdir(). + */ +typedef union { + INDEX_ROOT *ir; + INDEX_ALLOCATION *ia; +} index_union __attribute__((__transparent_union__)); + +/** + * enum INDEX_TYPE - + * More helpers for ntfs_readdir(). + */ +typedef enum { + INDEX_TYPE_ROOT, /* index root */ + INDEX_TYPE_ALLOCATION, /* index allocation */ +} INDEX_TYPE; + +/* + * Decode Interix file types + * + * Non-Interix types are returned as plain files, because a + * Windows user may force patterns very similar to Interix, + * and most metadata files have such similar patters. + */ + +static u32 ntfs_interix_types(ntfs_inode *ni) +{ + ntfs_attr *na; + u32 dt_type; + le64 magic; + + dt_type = NTFS_DT_UNKNOWN; + na = ntfs_attr_open(ni, AT_DATA, NULL, 0); + if (na) { + /* Unrecognized patterns (eg HID + SYST) are plain files */ + dt_type = NTFS_DT_REG; + if (na->data_size <= 1) { + if (!(ni->flags & FILE_ATTR_HIDDEN)) + dt_type = (na->data_size ? + NTFS_DT_SOCK : NTFS_DT_FIFO); + } else { + if ((na->data_size >= (s64)sizeof(magic)) + && (ntfs_attr_pread(na, 0, sizeof(magic), &magic) + == sizeof(magic))) { + if (magic == INTX_SYMBOLIC_LINK) + dt_type = NTFS_DT_LNK; + else if (magic == INTX_BLOCK_DEVICE) + dt_type = NTFS_DT_BLK; + else if (magic == INTX_CHARACTER_DEVICE) + dt_type = NTFS_DT_CHR; + } + } + ntfs_attr_close(na); + } + return (dt_type); +} + +/* + * Decode file types + * + * Better only use for Interix types and junctions, + * unneeded complexity when used for plain files or directories + * + * Error cases are logged and returned as unknown. + */ + +static u32 ntfs_dir_entry_type(ntfs_inode *dir_ni, MFT_REF mref, + FILE_ATTR_FLAGS attributes) +{ + ntfs_inode *ni; + u32 dt_type; + + dt_type = NTFS_DT_UNKNOWN; + ni = ntfs_inode_open(dir_ni->vol, mref); + if (ni) { + if ((attributes & FILE_ATTR_REPARSE_POINT) + && ntfs_possible_symlink(ni)) + dt_type = NTFS_DT_LNK; + else + if ((attributes & FILE_ATTR_SYSTEM) + && !(attributes & FILE_ATTR_I30_INDEX_PRESENT)) + dt_type = ntfs_interix_types(ni); + else + dt_type = (attributes + & FILE_ATTR_I30_INDEX_PRESENT + ? NTFS_DT_DIR : NTFS_DT_REG); + if (ntfs_inode_close(ni)) { + /* anything special worth doing ? */ + ntfs_log_error("Failed to close inode %lld\n", + (long long)MREF(mref)); + } + } + if (dt_type == NTFS_DT_UNKNOWN) + ntfs_log_error("Could not decode the type of inode %lld\n", + (long long)MREF(mref)); + return (dt_type); +} + +/** + * ntfs_filldir - ntfs specific filldir method + * @dir_ni: ntfs inode of current directory + * @pos: current position in directory + * @ivcn_bits: log(2) of index vcn size + * @index_type: specifies whether @iu is an index root or an index allocation + * @iu: index root or index block to which @ie belongs + * @ie: current index entry + * @dirent: context for filldir callback supplied by the caller + * @filldir: filldir callback supplied by the caller + * + * Pass information specifying the current directory entry @ie to the @filldir + * callback. + */ +static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, + const INDEX_TYPE index_type, index_union iu, INDEX_ENTRY *ie, + void *dirent, ntfs_filldir_t filldir) +{ + FILE_NAME_ATTR *fn = &ie->key.file_name; + unsigned dt_type; + BOOL metadata; + ntfschar *loname; + int res; + MFT_REF mref; + + ntfs_log_trace("Entering.\n"); + + /* Advance the position even if going to skip the entry. */ + if (index_type == INDEX_TYPE_ALLOCATION) + *pos = (u8*)ie - (u8*)iu.ia + (sle64_to_cpu( + iu.ia->index_block_vcn) << ivcn_bits) + + dir_ni->vol->mft_record_size; + else /* if (index_type == INDEX_TYPE_ROOT) */ + *pos = (u8*)ie - (u8*)iu.ir; + mref = le64_to_cpu(ie->indexed_file); + metadata = (MREF(mref) != FILE_root) && (MREF(mref) < FILE_first_user); + /* Skip root directory self reference entry. */ + if (MREF_LE(ie->indexed_file) == FILE_root) + return 0; + if ((ie->key.file_name.file_attributes + & (FILE_ATTR_REPARSE_POINT | FILE_ATTR_SYSTEM)) + && !metadata) + dt_type = ntfs_dir_entry_type(dir_ni, mref, + ie->key.file_name.file_attributes); + else if (ie->key.file_name.file_attributes + & FILE_ATTR_I30_INDEX_PRESENT) + dt_type = NTFS_DT_DIR; + else + dt_type = NTFS_DT_REG; + + /* return metadata files and hidden files if requested */ + if ((!metadata && (NVolShowHidFiles(dir_ni->vol) + || !(fn->file_attributes & FILE_ATTR_HIDDEN))) + || (NVolShowSysFiles(dir_ni->vol) && (NVolShowHidFiles(dir_ni->vol) + || metadata))) { + if (NVolCaseSensitive(dir_ni->vol)) { + res = filldir(dirent, fn->file_name, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + } else { + loname = (ntfschar*)ntfs_malloc(2*fn->file_name_length); + if (loname) { + memcpy(loname, fn->file_name, + 2*fn->file_name_length); + ntfs_name_locase(loname, fn->file_name_length, + dir_ni->vol->locase, + dir_ni->vol->upcase_len); + res = filldir(dirent, loname, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + free(loname); + } else + res = -1; + } + } else + res = 0; + return (res); +} + +/** + * ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode + * @ni: ntfs inode whose parent directory to find + * + * Find the parent directory of the ntfs inode @ni. To do this, find the first + * file name attribute in the mft record of @ni and return the parent mft + * reference from that. + * + * Note this only makes sense for directories, since files can be hard linked + * from multiple directories and there is no way for us to tell which one is + * being looked for. + * + * Technically directories can have hard links, too, but we consider that as + * illegal as Linux/UNIX do not support directory hard links. + * + * Return the mft reference of the parent directory on success or -1 on error + * with errno set to the error code. + */ +static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) +{ + MFT_REF mref; + ntfs_attr_search_ctx *ctx; + FILE_NAME_ATTR *fn; + int eo; + + ntfs_log_trace("Entering.\n"); + + if (!ni) { + errno = EINVAL; + return ERR_MREF(-1); + } + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return ERR_MREF(-1); + if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("No file name found in inode %lld\n", + (unsigned long long)ni->mft_no); + goto err_out; + } + if (ctx->attr->non_resident) { + ntfs_log_error("File name attribute must be resident (inode " + "%lld)\n", (unsigned long long)ni->mft_no); + goto io_err_out; + } + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) > + (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) { + ntfs_log_error("Corrupt file name attribute in inode %lld.\n", + (unsigned long long)ni->mft_no); + goto io_err_out; + } + mref = le64_to_cpu(fn->parent_directory); + ntfs_attr_put_search_ctx(ctx); + return mref; +io_err_out: + errno = EIO; +err_out: + eo = errno; + ntfs_attr_put_search_ctx(ctx); + errno = eo; + return ERR_MREF(-1); +} + +/** + * ntfs_readdir - read the contents of an ntfs directory + * @dir_ni: ntfs inode of current directory + * @pos: current position in directory + * @dirent: context for filldir callback supplied by the caller + * @filldir: filldir callback supplied by the caller + * + * Parse the index root and the index blocks that are marked in use in the + * index bitmap and hand each found directory entry to the @filldir callback + * supplied by the caller. + * + * Return 0 on success or -1 on error with errno set to the error code. + * + * Note: Index blocks are parsed in ascending vcn order, from which follows + * that the directory entries are not returned sorted. + */ +int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, + void *dirent, ntfs_filldir_t filldir) +{ + s64 i_size, br, ia_pos, bmp_pos, ia_start; + ntfs_volume *vol; + ntfs_attr *ia_na, *bmp_na = NULL; + ntfs_attr_search_ctx *ctx = NULL; + u8 *index_end, *bmp = NULL; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_ALLOCATION *ia = NULL; + int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo; + u32 index_block_size; + u8 index_block_size_bits, index_vcn_size_bits; + + ntfs_log_trace("Entering.\n"); + + if (!dir_ni || !pos || !filldir) { + errno = EINVAL; + return -1; + } + + if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + errno = ENOTDIR; + return -1; + } + + vol = dir_ni->vol; + + ntfs_log_trace("Entering for inode %lld, *pos 0x%llx.\n", + (unsigned long long)dir_ni->mft_no, (long long)*pos); + + /* Open the index allocation attribute. */ + ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (!ia_na) { + if (errno != ENOENT) { + ntfs_log_perror("Failed to open index allocation attribute. " + "Directory inode %lld is corrupt or bug", + (unsigned long long)dir_ni->mft_no); + return -1; + } + i_size = 0; + } else + i_size = ia_na->data_size; + + rc = 0; + + /* Are we at end of dir yet? */ + if (*pos >= i_size + vol->mft_record_size) + goto done; + + /* Emulate . and .. for all directories. */ + if (!*pos) { + rc = filldir(dirent, dotdot, 1, FILE_NAME_POSIX, *pos, + MK_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)), + NTFS_DT_DIR); + if (rc) + goto err_out; + ++*pos; + } + if (*pos == 1) { + MFT_REF parent_mref; + + parent_mref = ntfs_mft_get_parent_ref(dir_ni); + if (parent_mref == ERR_MREF(-1)) { + ntfs_log_perror("Parent directory not found"); + goto dir_err_out; + } + + rc = filldir(dirent, dotdot, 2, FILE_NAME_POSIX, *pos, + parent_mref, NTFS_DT_DIR); + if (rc) + goto err_out; + ++*pos; + } + + ctx = ntfs_attr_get_search_ctx(dir_ni, NULL); + if (!ctx) + goto err_out; + + /* Get the offset into the index root attribute. */ + ir_pos = (int)*pos; + /* Find the index root attribute in the mft record. */ + if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, + 0, ctx)) { + ntfs_log_perror("Index root attribute missing in directory inode " + "%lld", (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* Get to the index root value. */ + ir = (INDEX_ROOT*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + /* Determine the size of a vcn in the directory index. */ + index_block_size = le32_to_cpu(ir->index_block_size); + if (index_block_size < NTFS_BLOCK_SIZE || + index_block_size & (index_block_size - 1)) { + ntfs_log_error("Index block size %u is invalid.\n", + (unsigned)index_block_size); + goto dir_err_out; + } + index_block_size_bits = ffs(index_block_size) - 1; + if (vol->cluster_size <= index_block_size) { + index_vcn_size_bits = vol->cluster_size_bits; + } else { + index_vcn_size_bits = NTFS_BLOCK_SIZE_BITS; + } + + /* Are we jumping straight into the index allocation attribute? */ + if (*pos >= vol->mft_record_size) { + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + goto skip_index_root; + } + + index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ir->index + + le32_to_cpu(ir->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry or until filldir tells us it has had enough + * or signals an error (both covered by the rc test). + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + ntfs_log_debug("In index root, offset %d.\n", (int)((u8*)ie - (u8*)ir)); + /* Bounds checks. */ + if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) + goto dir_err_out; + /* The last entry cannot contain a name. */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + + /* Skip index root entry if continuing previous readdir. */ + if (ir_pos > (u8*)ie - (u8*)ir) + continue; + /* + * Submit the directory entry to ntfs_filldir(), which will + * invoke the filldir() callback as appropriate. + */ + rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits, + INDEX_TYPE_ROOT, ir, ie, dirent, filldir); + if (rc) { + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + goto err_out; + } + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + + /* If there is no index allocation attribute we are finished. */ + if (!ia_na) + goto EOD; + + /* Advance *pos to the beginning of the index allocation. */ + *pos = vol->mft_record_size; + +skip_index_root: + + if (!ia_na) + goto done; + + /* Allocate a buffer for the current index block. */ + ia = ntfs_malloc(index_block_size); + if (!ia) + goto err_out; + + bmp_na = ntfs_attr_open(dir_ni, AT_BITMAP, NTFS_INDEX_I30, 4); + if (!bmp_na) { + ntfs_log_perror("Failed to open index bitmap attribute"); + goto dir_err_out; + } + + /* Get the offset into the index allocation attribute. */ + ia_pos = *pos - vol->mft_record_size; + + bmp_pos = ia_pos >> index_block_size_bits; + if (bmp_pos >> 3 >= bmp_na->data_size) { + ntfs_log_error("Current index position exceeds index bitmap " + "size.\n"); + goto dir_err_out; + } + + bmp_buf_size = min(bmp_na->data_size - (bmp_pos >> 3), 4096); + bmp = ntfs_malloc(bmp_buf_size); + if (!bmp) + goto err_out; + + br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); + if (br != bmp_buf_size) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read from index bitmap attribute"); + goto err_out; + } + + bmp_buf_pos = 0; + /* If the index block is not in use find the next one that is. */ + while (!(bmp[bmp_buf_pos >> 3] & (1 << (bmp_buf_pos & 7)))) { +find_next_index_buffer: + bmp_pos++; + bmp_buf_pos++; + /* If we have reached the end of the bitmap, we are done. */ + if (bmp_pos >> 3 >= bmp_na->data_size) + goto EOD; + ia_pos = bmp_pos << index_block_size_bits; + if (bmp_buf_pos >> 3 < bmp_buf_size) + continue; + /* Read next chunk from the index bitmap. */ + bmp_buf_pos = 0; + if ((bmp_pos >> 3) + bmp_buf_size > bmp_na->data_size) + bmp_buf_size = bmp_na->data_size - (bmp_pos >> 3); + br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp); + if (br != bmp_buf_size) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read from index bitmap attribute"); + goto err_out; + } + } + + ntfs_log_debug("Handling index block 0x%llx.\n", (long long)bmp_pos); + + /* Read the index block starting at bmp_pos. */ + br = ntfs_attr_mst_pread(ia_na, bmp_pos << index_block_size_bits, 1, + index_block_size, ia); + if (br != 1) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read index block"); + goto err_out; + } + + ia_start = ia_pos & ~(s64)(index_block_size - 1); + if (sle64_to_cpu(ia->index_block_vcn) != ia_start >> + index_vcn_size_bits) { + ntfs_log_error("Actual VCN (0x%llx) of index buffer is different " + "from expected VCN (0x%llx) in inode 0x%llx.\n", + (long long)sle64_to_cpu(ia->index_block_vcn), + (long long)ia_start >> index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { + ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode %lld " + "has a size (%u) differing from the directory " + "specified size (%u).\n", (long long)ia_start >> + index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no, + (unsigned) le32_to_cpu(ia->index.allocated_size) + + 0x18, (unsigned)index_block_size); + goto dir_err_out; + } + index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); + if (index_end > (u8*)ia + index_block_size) { + ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode " + "%lld exceeds maximum size.\n", + (long long)ia_start >> index_vcn_size_bits, + (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* The first index entry. */ + ie = (INDEX_ENTRY*)((u8*)&ia->index + + le32_to_cpu(ia->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry or until ntfs_filldir tells us it has had + * enough or signals an error (both covered by the rc test). + */ + for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { + ntfs_log_debug("In index allocation, offset 0x%llx.\n", + (long long)ia_start + ((u8*)ie - (u8*)ia)); + /* Bounds checks. */ + if ((u8*)ie < (u8*)ia || (u8*)ie + + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8*)ie + le16_to_cpu(ie->key_length) > + index_end) { + ntfs_log_error("Index entry out of bounds in directory inode " + "%lld.\n", (unsigned long long)dir_ni->mft_no); + goto dir_err_out; + } + /* The last entry cannot contain a name. */ + if (ie->ie_flags & INDEX_ENTRY_END) + break; + + if (!le16_to_cpu(ie->length)) + goto dir_err_out; + + /* Skip index entry if continuing previous readdir. */ + if (ia_pos - ia_start > (u8*)ie - (u8*)ia) + continue; + /* + * Submit the directory entry to ntfs_filldir(), which will + * invoke the filldir() callback as appropriate. + */ + rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits, + INDEX_TYPE_ALLOCATION, ia, ie, dirent, filldir); + if (rc) + goto err_out; + } + goto find_next_index_buffer; +EOD: + /* We are finished, set *pos to EOD. */ + *pos = i_size + vol->mft_record_size; +done: + free(ia); + free(bmp); + if (bmp_na) + ntfs_attr_close(bmp_na); + if (ia_na) + ntfs_attr_close(ia_na); + ntfs_log_debug("EOD, *pos 0x%llx, returning 0.\n", (long long)*pos); + return 0; +dir_err_out: + errno = EIO; +err_out: + eo = errno; + ntfs_log_trace("failed.\n"); + if (ctx) + ntfs_attr_put_search_ctx(ctx); + free(ia); + free(bmp); + if (bmp_na) + ntfs_attr_close(bmp_na); + if (ia_na) + ntfs_attr_close(ia_na); + errno = eo; + return -1; +} + + +/** + * __ntfs_create - create object on ntfs volume + * @dir_ni: ntfs inode for directory in which create new object + * @securid: id of inheritable security descriptor, 0 if none + * @name: unicode name of new object + * @name_len: length of the name in unicode characters + * @type: type of the object to create + * @dev: major and minor device numbers (obtained from makedev()) + * @target: target in unicode (only for symlinks) + * @target_len: length of target in unicode characters + * + * Internal, use ntfs_create{,_device,_symlink} wrappers instead. + * + * @type can be: + * S_IFREG to create regular file + * S_IFDIR to create directory + * S_IFBLK to create block device + * S_IFCHR to create character device + * S_IFLNK to create symbolic link + * S_IFIFO to create FIFO + * S_IFSOCK to create socket + * other values are invalid. + * + * @dev is used only if @type is S_IFBLK or S_IFCHR, in other cases its value + * ignored. + * + * @target and @target_len are used only if @type is S_IFLNK, in other cases + * their value ignored. + * + * Return opened ntfs inode that describes created object on success or NULL + * on error with errno set to the error code. + */ +static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, + const ntfschar *name, u8 name_len, mode_t type, dev_t dev, + const ntfschar *target, int target_len) +{ + ntfs_inode *ni; + int rollback_data = 0, rollback_sd = 0; + FILE_NAME_ATTR *fn = NULL; + STANDARD_INFORMATION *si = NULL; + int err, fn_len, si_len; + + ntfs_log_trace("Entering.\n"); + + /* Sanity checks. */ + if (!dir_ni || !name || !name_len) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + return NULL; + } + + if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { + errno = EOPNOTSUPP; + return NULL; + } + + ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); + if (!ni) + return NULL; +#if CACHE_NIDATA_SIZE + ntfs_inode_invalidate(dir_ni->vol, ni->mft_no); +#endif + /* + * Create STANDARD_INFORMATION attribute. + * JPA Depending on available inherited security descriptor, + * Write STANDARD_INFORMATION v1.2 (no inheritance) or v3 + */ + if (securid) + si_len = sizeof(STANDARD_INFORMATION); + else + si_len = offsetof(STANDARD_INFORMATION, v1_end); + si = ntfs_calloc(si_len); + if (!si) { + err = errno; + goto err_out; + } + si->creation_time = ni->creation_time; + si->last_data_change_time = ni->last_data_change_time; + si->last_mft_change_time = ni->last_mft_change_time; + si->last_access_time = ni->last_access_time; + if (securid) { + set_nino_flag(ni, v3_Extensions); + ni->owner_id = si->owner_id = 0; + ni->security_id = si->security_id = securid; + ni->quota_charged = si->quota_charged = const_cpu_to_le64(0); + ni->usn = si->usn = const_cpu_to_le64(0); + } else + clear_nino_flag(ni, v3_Extensions); + if (!S_ISREG(type) && !S_ISDIR(type)) { + si->file_attributes = FILE_ATTR_SYSTEM; + ni->flags = FILE_ATTR_SYSTEM; + } + ni->flags |= FILE_ATTR_ARCHIVE; + if (NVolHideDotFiles(dir_ni->vol) + && (name_len > 1) + && (name[0] == const_cpu_to_le16('.')) + && (name[1] != const_cpu_to_le16('.'))) + ni->flags |= FILE_ATTR_HIDDEN; + /* + * Set compression flag according to parent directory + * unless NTFS version < 3.0 or cluster size > 4K + * or compression has been disabled + */ + if ((dir_ni->flags & FILE_ATTR_COMPRESSED) + && (dir_ni->vol->major_ver >= 3) + && NVolCompression(dir_ni->vol) + && (dir_ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) + && (S_ISREG(type) || S_ISDIR(type))) + ni->flags |= FILE_ATTR_COMPRESSED; + /* Add STANDARD_INFORMATION to inode. */ + if (ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, + (u8*)si, si_len)) { + err = errno; + ntfs_log_error("Failed to add STANDARD_INFORMATION " + "attribute.\n"); + goto err_out; + } + + if (!securid) { + if (ntfs_sd_add_everyone(ni)) { + err = errno; + goto err_out; + } + } + rollback_sd = 1; + + if (S_ISDIR(type)) { + INDEX_ROOT *ir = NULL; + INDEX_ENTRY *ie; + int ir_len, index_len; + + /* Create INDEX_ROOT attribute. */ + index_len = sizeof(INDEX_HEADER) + sizeof(INDEX_ENTRY_HEADER); + ir_len = offsetof(INDEX_ROOT, index) + index_len; + ir = ntfs_calloc(ir_len); + if (!ir) { + err = errno; + goto err_out; + } + ir->type = AT_FILE_NAME; + ir->collation_rule = COLLATION_FILE_NAME; + ir->index_block_size = cpu_to_le32(ni->vol->indx_record_size); + if (ni->vol->cluster_size <= ni->vol->indx_record_size) + ir->clusters_per_index_block = + ni->vol->indx_record_size >> + ni->vol->cluster_size_bits; + else + ir->clusters_per_index_block = + ni->vol->indx_record_size >> + NTFS_BLOCK_SIZE_BITS; + ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER)); + ir->index.index_length = cpu_to_le32(index_len); + ir->index.allocated_size = cpu_to_le32(index_len); + ie = (INDEX_ENTRY*)((u8*)ir + sizeof(INDEX_ROOT)); + ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); + ie->key_length = 0; + ie->ie_flags = INDEX_ENTRY_END; + /* Add INDEX_ROOT attribute to inode. */ + if (ntfs_attr_add(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4, + (u8*)ir, ir_len)) { + err = errno; + free(ir); + ntfs_log_error("Failed to add INDEX_ROOT attribute.\n"); + goto err_out; + } + free(ir); + } else { + INTX_FILE *data; + int data_len; + + switch (type) { + case S_IFBLK: + case S_IFCHR: + data_len = offsetof(INTX_FILE, device_end); + data = ntfs_malloc(data_len); + if (!data) { + err = errno; + goto err_out; + } + data->major = cpu_to_le64(major(dev)); + data->minor = cpu_to_le64(minor(dev)); + if (type == S_IFBLK) + data->magic = INTX_BLOCK_DEVICE; + if (type == S_IFCHR) + data->magic = INTX_CHARACTER_DEVICE; + break; + case S_IFLNK: + data_len = sizeof(INTX_FILE_TYPES) + + target_len * sizeof(ntfschar); + data = ntfs_malloc(data_len); + if (!data) { + err = errno; + goto err_out; + } + data->magic = INTX_SYMBOLIC_LINK; + memcpy(data->target, target, + target_len * sizeof(ntfschar)); + break; + case S_IFSOCK: + data = NULL; + data_len = 1; + break; + default: /* FIFO or regular file. */ + data = NULL; + data_len = 0; + break; + } + /* Add DATA attribute to inode. */ + if (ntfs_attr_add(ni, AT_DATA, AT_UNNAMED, 0, (u8*)data, + data_len)) { + err = errno; + ntfs_log_error("Failed to add DATA attribute.\n"); + free(data); + goto err_out; + } + rollback_data = 1; + free(data); + } + /* Create FILE_NAME attribute. */ + fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); + fn = ntfs_calloc(fn_len); + if (!fn) { + err = errno; + goto err_out; + } + fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)); + fn->file_name_length = name_len; + fn->file_name_type = FILE_NAME_POSIX; + if (S_ISDIR(type)) + fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT; + if (!S_ISREG(type) && !S_ISDIR(type)) + fn->file_attributes = FILE_ATTR_SYSTEM; + else + fn->file_attributes |= ni->flags & FILE_ATTR_COMPRESSED; + fn->file_attributes |= FILE_ATTR_ARCHIVE; + fn->file_attributes |= ni->flags & FILE_ATTR_HIDDEN; + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + else { + fn->data_size = cpu_to_sle64(ni->data_size); + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + } + memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); + /* Add FILE_NAME attribute to inode. */ + if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { + err = errno; + ntfs_log_error("Failed to add FILE_NAME attribute.\n"); + goto err_out; + } + /* Add FILE_NAME attribute to index. */ + if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)))) { + err = errno; + ntfs_log_perror("Failed to add entry to the index"); + goto err_out; + } + /* Set hard links count and directory flag. */ + ni->mrec->link_count = cpu_to_le16(1); + if (S_ISDIR(type)) + ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY; + ntfs_inode_mark_dirty(ni); + /* Done! */ + free(fn); + free(si); + ntfs_log_trace("Done.\n"); + return ni; +err_out: + ntfs_log_trace("Failed.\n"); + + if (rollback_sd) + ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); + + if (rollback_data) + ntfs_attr_remove(ni, AT_DATA, AT_UNNAMED, 0); + /* + * Free extent MFT records (should not exist any with current + * ntfs_create implementation, but for any case if something will be + * changed in the future). + */ + while (ni->nr_extents) + if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } + if (ntfs_mft_record_free(ni->vol, ni)) + ntfs_log_error("Failed to free MFT record. " + "Leaving inconsistent metadata. Run chkdsk.\n"); + free(fn); + free(si); + errno = err; + return NULL; +} + +/** + * Some wrappers around __ntfs_create() ... + */ + +ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, const ntfschar *name, + u8 name_len, mode_t type) +{ + if (type != S_IFREG && type != S_IFDIR && type != S_IFIFO && + type != S_IFSOCK) { + ntfs_log_error("Invalid arguments.\n"); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, type, 0, NULL, 0); +} + +ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid, + const ntfschar *name, u8 name_len, mode_t type, dev_t dev) +{ + if (type != S_IFCHR && type != S_IFBLK) { + ntfs_log_error("Invalid arguments.\n"); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, type, dev, NULL, 0); +} + +ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid, + const ntfschar *name, u8 name_len, const ntfschar *target, + int target_len) +{ + if (!target || !target_len) { + ntfs_log_error("%s: Invalid argument (%p, %d)\n", __FUNCTION__, + target, target_len); + return NULL; + } + return __ntfs_create(dir_ni, securid, name, name_len, S_IFLNK, 0, + target, target_len); +} + +int ntfs_check_empty_dir(ntfs_inode *ni) +{ + ntfs_attr *na; + int ret = 0; + + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) + return 0; + + na = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4); + if (!na) { + errno = EIO; + ntfs_log_perror("Failed to open directory"); + return -1; + } + + /* Non-empty directory? */ + if ((na->data_size != sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER))){ + /* Both ENOTEMPTY and EEXIST are ok. We use the more common. */ + errno = ENOTEMPTY; + ntfs_log_debug("Directory is not empty\n"); + ret = -1; + } + + ntfs_attr_close(na); + return ret; +} + +static int ntfs_check_unlinkable_dir(ntfs_inode *ni, FILE_NAME_ATTR *fn) +{ + int link_count = le16_to_cpu(ni->mrec->link_count); + int ret; + + ret = ntfs_check_empty_dir(ni); + if (!ret || errno != ENOTEMPTY) + return ret; + /* + * Directory is non-empty, so we can unlink only if there is more than + * one "real" hard link, i.e. links aren't different DOS and WIN32 names + */ + if ((link_count == 1) || + (link_count == 2 && fn->file_name_type == FILE_NAME_DOS)) { + errno = ENOTEMPTY; + ntfs_log_debug("Non-empty directory without hard links\n"); + goto no_hardlink; + } + + ret = 0; +no_hardlink: + return ret; +} + +/** + * ntfs_delete - delete file or directory from ntfs volume + * @ni: ntfs inode for object to delte + * @dir_ni: ntfs inode for directory in which delete object + * @name: unicode name of the object to delete + * @name_len: length of the name in unicode characters + * + * @ni is always closed after the call to this function (even if it failed), + * user does not need to call ntfs_inode_close himself. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_delete(ntfs_volume *vol, const char *pathname, + ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, + u8 name_len) +{ + ntfs_attr_search_ctx *actx = NULL; + FILE_NAME_ATTR *fn = NULL; + BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; + BOOL case_sensitive_match = TRUE; + int err = 0; +#if CACHE_NIDATA_SIZE + int i; +#endif +#if CACHE_INODE_SIZE + struct CACHED_INODE item; + const char *p; + u64 inum = (u64)-1; + int count; +#endif +#if CACHE_LOOKUP_SIZE + struct CACHED_LOOKUP lkitem; +#endif + + ntfs_log_trace("Entering.\n"); + + if (!ni || !dir_ni || !name || !name_len) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + goto err_out; + } + if (ni->nr_extents == -1) + ni = ni->base_ni; + if (dir_ni->nr_extents == -1) + dir_ni = dir_ni->base_ni; + /* + * Search for FILE_NAME attribute with such name. If it's in POSIX or + * WIN32_AND_DOS namespace, then simply remove it from index and inode. + * If filename in DOS or in WIN32 namespace, then remove DOS name first, + * only then remove WIN32 name. + */ + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (!actx) + goto err_out; +search: + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, actx)) { + char *s; + IGNORE_CASE_BOOL case_sensitive = IGNORE_CASE; + + errno = 0; + fn = (FILE_NAME_ATTR*)((u8*)actx->attr + + le16_to_cpu(actx->attr->value_offset)); + s = ntfs_attr_name_get(fn->file_name, fn->file_name_length); + ntfs_log_trace("name: '%s' type: %d dos: %d win32: %d " + "case: %d\n", s, fn->file_name_type, + looking_for_dos_name, looking_for_win32_name, + case_sensitive_match); + ntfs_attr_name_free(&s); + if (looking_for_dos_name) { + if (fn->file_name_type == FILE_NAME_DOS) + break; + else + continue; + } + if (looking_for_win32_name) { + if (fn->file_name_type == FILE_NAME_WIN32) + break; + else + continue; + } + + /* Ignore hard links from other directories */ + if (dir_ni->mft_no != MREF_LE(fn->parent_directory)) { + ntfs_log_debug("MFT record numbers don't match " + "(%llu != %llu)\n", + (long long unsigned)dir_ni->mft_no, + (long long unsigned)MREF_LE(fn->parent_directory)); + continue; + } + if (case_sensitive_match + || ((fn->file_name_type == FILE_NAME_POSIX) + && NVolCaseSensitive(ni->vol))) + case_sensitive = CASE_SENSITIVE; + + if (ntfs_names_are_equal(fn->file_name, fn->file_name_length, + name, name_len, case_sensitive, + ni->vol->upcase, ni->vol->upcase_len)){ + + if (fn->file_name_type == FILE_NAME_WIN32) { + looking_for_dos_name = TRUE; + ntfs_attr_reinit_search_ctx(actx); + continue; + } + if (fn->file_name_type == FILE_NAME_DOS) + looking_for_dos_name = TRUE; + break; + } + } + if (errno) { + /* + * If case sensitive search failed, then try once again + * ignoring case. + */ + if (errno == ENOENT && case_sensitive_match) { + case_sensitive_match = FALSE; + ntfs_attr_reinit_search_ctx(actx); + goto search; + } + goto err_out; + } + + if (ntfs_check_unlinkable_dir(ni, fn) < 0) + goto err_out; + + if (ntfs_index_remove(dir_ni, ni, fn, le32_to_cpu(actx->attr->value_length))) + goto err_out; + + /* + * Keep the last name in place, this is useful for undeletion + * (Windows also does so), however delete the name if it were + * in an extent, to avoid leaving an attribute list. + */ + if ((ni->mrec->link_count == cpu_to_le16(1)) && !actx->base_ntfs_ino) { + /* make sure to not loop to another search */ + looking_for_dos_name = FALSE; + } else { + if (ntfs_attr_record_rm(actx)) + goto err_out; + } + + ni->mrec->link_count = cpu_to_le16(le16_to_cpu( + ni->mrec->link_count) - 1); + + ntfs_inode_mark_dirty(ni); + if (looking_for_dos_name) { + looking_for_dos_name = FALSE; + looking_for_win32_name = TRUE; + ntfs_attr_reinit_search_ctx(actx); + goto search; + } + /* TODO: Update object id, quota and securiry indexes if required. */ + /* + * If hard link count is not equal to zero then we are done. In other + * case there are no reference to this inode left, so we should free all + * non-resident attributes and mark all MFT record as not in use. + */ +#if CACHE_LOOKUP_SIZE + /* invalidate entry in lookup cache */ + lkitem.name = (const char*)NULL; + lkitem.namesize = 0; + lkitem.inum = ni->mft_no; + lkitem.parent = dir_ni->mft_no; + ntfs_invalidate_cache(vol->lookup_cache, GENERIC(&lkitem), + lookup_cache_inv_compare, CACHE_NOHASH); +#endif +#if CACHE_INODE_SIZE + inum = ni->mft_no; + if (pathname) { + /* invalide cache entry, even if there was an error */ + /* Remove leading /'s. */ + p = pathname; + while (*p == PATH_SEP) + p++; + if (p[0] && (p[strlen(p)-1] == PATH_SEP)) + ntfs_log_error("Unnormalized path %s\n",pathname); + item.pathname = p; + item.varsize = strlen(p); + } else { + item.pathname = (const char*)NULL; + item.varsize = 0; + } + item.inum = inum; + count = ntfs_invalidate_cache(vol->xinode_cache, GENERIC(&item), + inode_cache_inv_compare, CACHE_NOHASH); + if (pathname && !count) + ntfs_log_error("Could not delete inode cache entry for %s\n", + pathname); +#endif + if (ni->mrec->link_count) { + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); + goto ok; + } + if (ntfs_delete_reparse_index(ni)) { + /* + * Failed to remove the reparse index : proceed anyway + * This is not a critical error, the entry is useless + * because of sequence_number, and stopping file deletion + * would be much worse as the file is not referenced now. + */ + err = errno; + } + if (ntfs_delete_object_id_index(ni)) { + /* + * Failed to remove the object id index : proceed anyway + * This is not a critical error. + */ + err = errno; + } + ntfs_attr_reinit_search_ctx(actx); + while (!ntfs_attrs_walk(actx)) { + if (actx->attr->non_resident) { + runlist *rl; + + rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr, + NULL); + if (!rl) { + err = errno; + ntfs_log_error("Failed to decompress runlist. " + "Leaving inconsistent metadata.\n"); + continue; + } + if (ntfs_cluster_free_from_rl(ni->vol, rl)) { + err = errno; + ntfs_log_error("Failed to free clusters. " + "Leaving inconsistent metadata.\n"); + continue; + } + free(rl); + } + } + if (errno != ENOENT) { + err = errno; + ntfs_log_error("Attribute enumeration failed. " + "Probably leaving inconsistent metadata.\n"); + } + /* All extents should be attached after attribute walk. */ +#if CACHE_NIDATA_SIZE + /* + * Disconnect extents before deleting them, so they are + * not wrongly moved to cache through the chainings + */ + for (i=ni->nr_extents-1; i>=0; i--) { + ni->extent_nis[i]->base_ni = (ntfs_inode*)NULL; + ni->extent_nis[i]->nr_extents = 0; + if (ntfs_mft_record_free(ni->vol, ni->extent_nis[i])) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } + } + free(ni->extent_nis); + ni->nr_extents = 0; + ni->extent_nis = (ntfs_inode**)NULL; +#else + while (ni->nr_extents) + if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } +#endif + debug_double_inode(ni->mft_no,0); + if (ntfs_mft_record_free(ni->vol, ni)) { + err = errno; + ntfs_log_error("Failed to free base MFT record. " + "Leaving inconsistent metadata.\n"); + } + ni = NULL; +ok: + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); +out: + if (actx) + ntfs_attr_put_search_ctx(actx); + if (ntfs_inode_close(dir_ni) && !err) + err = errno; + if (ntfs_inode_close(ni) && !err) + err = errno; + if (err) { + errno = err; + ntfs_log_debug("Could not delete file: %s\n", strerror(errno)); + return -1; + } + ntfs_log_trace("Done.\n"); + return 0; +err_out: + err = errno; + goto out; +} + +/** + * ntfs_link - create hard link for file or directory + * @ni: ntfs inode for object to create hard link + * @dir_ni: ntfs inode for directory in which new link should be placed + * @name: unicode name of the new link + * @name_len: length of the name in unicode characters + * + * NOTE: At present we allow creating hardlinks to directories, we use them + * in a temporary state during rename. But it's defenitely bad idea to have + * hard links to directories as a result of operation. + * FIXME: Create internal __ntfs_link that allows hard links to a directories + * and external ntfs_link that do not. Write ntfs_rename that uses __ntfs_link. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, + u8 name_len, FILE_NAME_TYPE_FLAGS nametype) +{ + FILE_NAME_ATTR *fn = NULL; + int fn_len, err; + + ntfs_log_trace("Entering.\n"); + + if (!ni || !dir_ni || !name || !name_len || + ni->mft_no == dir_ni->mft_no) { + err = EINVAL; + ntfs_log_perror("ntfs_link wrong arguments"); + goto err_out; + } + + if ((ni->flags & FILE_ATTR_REPARSE_POINT) + && !ntfs_possible_symlink(ni)) { + err = EOPNOTSUPP; + goto err_out; + } + if (NVolHideDotFiles(dir_ni->vol)) { + /* Set hidden flag according to the latest name */ + if ((name_len > 1) + && (name[0] == const_cpu_to_le16('.')) + && (name[1] != const_cpu_to_le16('.'))) + ni->flags |= FILE_ATTR_HIDDEN; + else + ni->flags &= ~FILE_ATTR_HIDDEN; + } + + /* Create FILE_NAME attribute. */ + fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); + fn = ntfs_calloc(fn_len); + if (!fn) { + err = errno; + goto err_out; + } + fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)); + fn->file_name_length = name_len; + fn->file_name_type = nametype; + fn->file_attributes = ni->flags; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT; + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + } else { + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + fn->data_size = cpu_to_sle64(ni->data_size); + } + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; + memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); + /* Add FILE_NAME attribute to index. */ + if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)))) { + err = errno; + ntfs_log_perror("Failed to add filename to the index"); + goto err_out; + } + /* Add FILE_NAME attribute to inode. */ + if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { + ntfs_log_error("Failed to add FILE_NAME attribute.\n"); + err = errno; + /* Try to remove just added attribute from index. */ + if (ntfs_index_remove(dir_ni, ni, fn, fn_len)) + goto rollback_failed; + goto err_out; + } + /* Increment hard links count. */ + ni->mrec->link_count = cpu_to_le16(le16_to_cpu( + ni->mrec->link_count) + 1); + /* Done! */ + ntfs_inode_mark_dirty(ni); + free(fn); + ntfs_log_trace("Done.\n"); + return 0; +rollback_failed: + ntfs_log_error("Rollback failed. Leaving inconsistent metadata.\n"); +err_out: + free(fn); + errno = err; + return -1; +} + +int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, + u8 name_len) +{ + return (ntfs_link_i(ni, dir_ni, name, name_len, FILE_NAME_POSIX)); +} + +/* + * Get a parent directory from an inode entry + * + * This is only used in situations where the path used to access + * the current file is not known for sure. The result may be different + * from the path when the file is linked in several parent directories. + * + * Currently this is only used for translating ".." in the target + * of a Vista relative symbolic link + */ + +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni) +{ + ntfs_inode *dir_ni = (ntfs_inode*)NULL; + u64 inum; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + if (ni->mft_no != FILE_root) { + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return ((ntfs_inode*)NULL); + + if (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + inum = le64_to_cpu(fn->parent_directory); + if (inum != (u64)-1) { + dir_ni = ntfs_inode_open(ni->vol, MREF(inum)); + } + } + ntfs_attr_put_search_ctx(ctx); + } + return (dir_ni); +} + +#ifdef HAVE_SETXATTR + +#define MAX_DOS_NAME_LENGTH 12 + +/* + * Get a DOS name for a file in designated directory + * + * Not allowed if there are several non-dos names (EMLINK) + * + * Returns size if found + * 0 if not found + * -1 if there was an error (described by errno) + */ + +static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname) +{ + size_t outsize = 0; + int namecount = 0; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if (fn->file_name_type != FILE_NAME_DOS) + namecount++; + if ((fn->file_name_type & FILE_NAME_DOS) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a DOS or WIN32+DOS name for the entry + * copy name, after truncation for safety + */ + outsize = fn->file_name_length; +/* TODO : reject if name is too long ? */ + if (outsize > MAX_DOS_NAME_LENGTH) + outsize = MAX_DOS_NAME_LENGTH; + memcpy(dosname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + ntfs_attr_put_search_ctx(ctx); + if ((outsize > 0) && (namecount > 1)) { + outsize = -1; + errno = EMLINK; /* this error implies there is a dos name */ + } + return (outsize); +} + + +/* + * Get a long name for a file in designated directory + * + * Not allowed if there are several non-dos names (EMLINK) + * + * Returns size if found + * 0 if not found + * -1 if there was an error (described by errno) + */ + +static int get_long_name(ntfs_inode *ni, u64 dnum, ntfschar *longname) +{ + size_t outsize = 0; + int namecount = 0; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + + /* first search for WIN32 or DOS+WIN32 names */ + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if (fn->file_name_type != FILE_NAME_DOS) + namecount++; + if ((fn->file_name_type & FILE_NAME_WIN32) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a WIN32 or WIN32+DOS name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + if (namecount > 1) { + ntfs_attr_put_search_ctx(ctx); + errno = EMLINK; + return -1; + } + /* if not found search for POSIX names */ + if (!outsize) { + ntfs_attr_reinit_search_ctx(ctx); + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if ((fn->file_name_type == FILE_NAME_POSIX) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a POSIX name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + } + ntfs_attr_put_search_ctx(ctx); + return (outsize); +} + + +/* + * Get the ntfs DOS name into an extended attribute + */ + +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size) +{ + int outsize = 0; + char *outname = (char*)NULL; + u64 dnum; + int doslen; + ntfschar dosname[MAX_DOS_NAME_LENGTH]; + + dnum = dir_ni->mft_no; + doslen = get_dos_name(ni, dnum, dosname); + if (doslen > 0) { + /* + * Found a DOS name for the entry, make + * uppercase and encode into the buffer + * if there is enough space + */ + ntfs_name_upcase(dosname, doslen, + ni->vol->upcase, ni->vol->upcase_len); + if (ntfs_ucstombs(dosname, doslen, &outname, size) < 0) { + ntfs_log_error("Cannot represent dosname in current locale.\n"); + outsize = -errno; + } else { + outsize = strlen(outname); + if (value && (outsize <= (int)size)) + memcpy(value, outname, outsize); + else + if (size && (outsize > (int)size)) + outsize = -ERANGE; + free(outname); + } + } else { + if (doslen == 0) + errno = ENODATA; + outsize = -errno; + } + return (outsize); +} + +/* + * Change the name space of an existing file or directory + * + * Returns the old namespace if successful + * -1 if an error occurred (described by errno) + */ + +static int set_namespace(ntfs_inode *ni, ntfs_inode *dir_ni, + const ntfschar *name, int len, + FILE_NAME_TYPE_FLAGS nametype) +{ + ntfs_attr_search_ctx *actx; + ntfs_index_context *icx; + FILE_NAME_ATTR *fnx; + FILE_NAME_ATTR *fn = NULL; + BOOL found; + int lkup; + int ret; + + ret = -1; + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (actx) { + found = FALSE; + do { + lkup = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, actx); + if (!lkup) { + fn = (FILE_NAME_ATTR*)((u8*)actx->attr + + le16_to_cpu(actx->attr->value_offset)); + found = (MREF_LE(fn->parent_directory) + == dir_ni->mft_no) + && !memcmp(fn->file_name, name, + len*sizeof(ntfschar)); + } + } while (!lkup && !found); + if (found) { + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (icx) { + lkup = ntfs_index_lookup((char*)fn, len, icx); + if (!lkup && icx->data && icx->data_len) { + fnx = (FILE_NAME_ATTR*)icx->data; + ret = fn->file_name_type; + fn->file_name_type = nametype; + fnx->file_name_type = nametype; + ntfs_inode_mark_dirty(ni); + ntfs_index_entry_mark_dirty(icx); + } + ntfs_index_ctx_put(icx); + } + } + ntfs_attr_put_search_ctx(actx); + } + return (ret); +} + +/* + * Set a DOS name to a file and adjust name spaces + * + * If the new names are collapsible (same uppercased chars) : + * + * - the existing DOS name or DOS+Win32 name is made Posix + * - if it was a real DOS name, the existing long name is made DOS+Win32 + * and the existing DOS name is deleted + * - finally the existing long name is made DOS+Win32 unless already done + * + * If the new names are not collapsible : + * + * - insert the short name as a DOS name + * - delete the old long name or existing short name + * - insert the new long name (as a Win32 or DOS+Win32 name) + * + * Deleting the old long name will not delete the file + * provided the old name was in the Posix name space, + * because the alternate name has been set before. + * + * The inodes of file and parent directory are always closed + * + * Returns 0 if successful + * -1 if failed + */ + +static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const ntfschar *shortname, int shortlen, + const ntfschar *longname, int longlen, + const ntfschar *deletename, int deletelen, BOOL existed) +{ + unsigned int linkcount; + ntfs_volume *vol; + BOOL collapsible; + BOOL deleted; + BOOL done; + FILE_NAME_TYPE_FLAGS oldnametype; + u64 dnum; + u64 fnum; + int res; + + res = -1; + vol = ni->vol; + dnum = dir_ni->mft_no; + fnum = ni->mft_no; + /* save initial link count */ + linkcount = le16_to_cpu(ni->mrec->link_count); + + /* check whether the same name may be used as DOS and WIN32 */ + collapsible = ntfs_collapsible_chars(ni->vol, shortname, shortlen, + longname, longlen); + if (collapsible) { + deleted = FALSE; + done = FALSE; + if (existed) { + oldnametype = set_namespace(ni, dir_ni, deletename, + deletelen, FILE_NAME_POSIX); + if (oldnametype == FILE_NAME_DOS) { + if (set_namespace(ni, dir_ni, longname, longlen, + FILE_NAME_WIN32_AND_DOS) >= 0) { + if (!ntfs_delete(vol, + (const char*)NULL, ni, dir_ni, + deletename, deletelen)) + res = 0; + deleted = TRUE; + } else + done = TRUE; + } + } + if (!deleted) { + if (!done && (set_namespace(ni, dir_ni, + longname, longlen, + FILE_NAME_WIN32_AND_DOS) >= 0)) + res = 0; + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); + if (ntfs_inode_close_in_dir(ni,dir_ni) && !res) + res = -1; + if (ntfs_inode_close(dir_ni) && !res) + res = -1; + } + } else { + if (!ntfs_link_i(ni, dir_ni, shortname, shortlen, + FILE_NAME_DOS) + /* make sure a new link was recorded */ + && (le16_to_cpu(ni->mrec->link_count) > linkcount)) { + /* delete the existing long name or short name */ +// is it ok to not provide the path ? + if (!ntfs_delete(vol, (char*)NULL, ni, dir_ni, + deletename, deletelen)) { + /* delete closes the inodes, so have to open again */ + dir_ni = ntfs_inode_open(vol, dnum); + if (dir_ni) { + ni = ntfs_inode_open(vol, fnum); + if (ni) { + if (!ntfs_link_i(ni, dir_ni, + longname, longlen, + FILE_NAME_WIN32)) + res = 0; + if (ntfs_inode_close_in_dir(ni, + dir_ni) + && !res) + res = -1; + } + if (ntfs_inode_close(dir_ni) && !res) + res = -1; + } + } + } else { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + } + return (res); +} + + +/* + * Set the ntfs DOS name into an extended attribute + * + * The DOS name will be added as another file name attribute + * using the existing file name information from the original + * name or overwriting the DOS Name if one exists. + * + * The inode of the file is always closed + */ + +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags) +{ + int res = 0; + int longlen = 0; + int shortlen = 0; + char newname[3*MAX_DOS_NAME_LENGTH + 1]; + ntfschar oldname[MAX_DOS_NAME_LENGTH]; + int oldlen; + u64 dnum; + BOOL closed = FALSE; + ntfschar *shortname = NULL; + ntfschar longname[NTFS_MAX_NAME_LEN]; + + /* copy the string to insert a null char, and truncate */ + if (size > 3*MAX_DOS_NAME_LENGTH) + size = 3*MAX_DOS_NAME_LENGTH; + strncpy(newname, value, size); + /* a long name may be truncated badly and be untranslatable */ + newname[size] = 0; + /* convert the string to the NTFS wide chars, and truncate */ + shortlen = ntfs_mbstoucs(newname, &shortname); + if (shortlen > MAX_DOS_NAME_LENGTH) + shortlen = MAX_DOS_NAME_LENGTH; + /* make sure the short name has valid chars */ + if ((shortlen < 0) + || ntfs_forbidden_names(ni->vol,shortname,shortlen)) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + res = -errno; + return res; + } + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { + oldlen = get_dos_name(ni, dnum, oldname); + if ((oldlen >= 0) + && !ntfs_forbidden_names(ni->vol, longname, longlen)) { + if (oldlen > 0) { + if (flags & XATTR_CREATE) { + res = -1; + errno = EEXIST; + } else + if ((shortlen == oldlen) + && !memcmp(shortname,oldname, + oldlen*sizeof(ntfschar))) + /* already set, done */ + res = 0; + else { + res = set_dos_name(ni, dir_ni, + shortname, shortlen, + longname, longlen, + oldname, oldlen, TRUE); + closed = TRUE; + } + } else { + if (flags & XATTR_REPLACE) { + res = -1; + errno = ENODATA; + } else { + res = set_dos_name(ni, dir_ni, + shortname, shortlen, + longname, longlen, + longname, longlen, FALSE); + closed = TRUE; + } + } + } else + res = -1; + } else { + res = -1; + if (!longlen) + errno = ENOENT; + } + free(shortname); + if (!closed) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + return (res ? -1 : 0); +} + +/* + * Delete the ntfs DOS name + */ + +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + int oldnametype; + int longlen = 0; + int shortlen; + u64 dnum; + ntfs_volume *vol; + BOOL deleted = FALSE; + ntfschar shortname[MAX_DOS_NAME_LENGTH]; + ntfschar longname[NTFS_MAX_NAME_LEN]; + + res = -1; + vol = ni->vol; + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { + shortlen = get_dos_name(ni, dnum, shortname); + if (shortlen >= 0) { + /* migrate the long name as Posix */ + oldnametype = set_namespace(ni,dir_ni,longname,longlen, + FILE_NAME_POSIX); + switch (oldnametype) { + case FILE_NAME_WIN32_AND_DOS : + /* name was Win32+DOS : done */ + res = 0; + break; + case FILE_NAME_DOS : + /* name was DOS, make it back to DOS */ + set_namespace(ni,dir_ni,longname,longlen, + FILE_NAME_DOS); + errno = ENOENT; + break; + case FILE_NAME_WIN32 : + /* name was Win32, make it Posix and delete */ + if (set_namespace(ni,dir_ni,shortname,shortlen, + FILE_NAME_POSIX) >= 0) { + if (!ntfs_delete(vol, + (const char*)NULL, ni, + dir_ni, shortname, + shortlen)) + res = 0; + deleted = TRUE; + } else { + /* + * DOS name has been found, but cannot + * migrate to Posix : something bad + * has happened + */ + errno = EIO; + ntfs_log_error("Could not change" + " DOS name of inode %lld to Posix\n", + (long long)ni->mft_no); + } + break; + default : + /* name was Posix or not found : error */ + errno = ENOENT; + break; + } + } + } else { + if (!longlen) + errno = ENOENT; + res = -1; + } + if (!deleted) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } + return (res); +} + +#endif diff --git a/libntfs-3g/ea.c b/libntfs-3g/ea.c new file mode 100755 index 0000000000000000000000000000000000000000..d07e111b843b809f53e7d91f78a3a7d632bf8ff5 --- /dev/null +++ b/libntfs-3g/ea.c @@ -0,0 +1,402 @@ +/** + * ea.c - Processing of EA's + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2014 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SETXATTR /* extended attributes support required */ + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#endif + +#include "types.h" +#include "param.h" +#include "layout.h" +#include "attrib.h" +#include "index.h" +#include "dir.h" +#include "ea.h" +#include "misc.h" +#include "logging.h" + +/* + * Create a needed attribute (EA or EA_INFORMATION) + * + * Returns 0 if successful, + * -1 otherwise, with errno indicating why it failed. + */ + +static int ntfs_need_ea(ntfs_inode *ni, ATTR_TYPES type, int size, int flags) +{ + u8 dummy; + int res; + + res = 0; + if (!ntfs_attr_exist(ni,type, AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no needed attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, type, + AT_UNNAMED,0,&dummy,(s64)size); + if (!res) { + NInoFileNameSetDirty(ni); + } + NInoSetDirty(ni); + } else { + errno = EOPNOTSUPP; + res = -1; + } + } else { + errno = ENODATA; + res = -1; + } + } + return (res); +} + +/* + * Restore the old EA_INFORMATION or delete the current one, + * when EA cannot be updated. + * + * As this is used in the context of some other error, the caller + * is responsible for returning the proper error, and errno is + * left unchanged. + * Only double errors are logged here. + */ + +static void restore_ea_info(ntfs_attr *nai, const EA_INFORMATION *old_ea_info) +{ + s64 written; + int olderrno; + + olderrno = errno; + if (old_ea_info) { + written = ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION), + old_ea_info); + if ((size_t)written != sizeof(EA_INFORMATION)) { + ntfs_log_error("Could not restore the EA_INFORMATION," + " possible inconsistency in inode %lld\n", + (long long)nai->ni->mft_no); + } + } else { + if (ntfs_attr_rm(nai)) { + ntfs_log_error("Could not delete the EA_INFORMATION," + " possible inconsistency in inode %lld\n", + (long long)nai->ni->mft_no); + } + } + errno = olderrno; +} + +/* + * Update both EA and EA_INFORMATION + */ + +static int ntfs_update_ea(ntfs_inode *ni, const char *value, size_t size, + const EA_INFORMATION *ea_info, + const EA_INFORMATION *old_ea_info) +{ + ntfs_attr *na; + ntfs_attr *nai; + int res; + + res = 0; + nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0); + if (nai) { + na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0); + if (na) { + /* + * Set EA_INFORMATION first, it is easier to + * restore the old value, if setting EA fails. + */ + if (ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION), + ea_info) + != (s64)sizeof(EA_INFORMATION)) { + res = -errno; + } else { + if (((na->data_size > (s64)size) + && ntfs_attr_truncate(na, size)) + || (ntfs_attr_pwrite(na, 0, size, value) + != (s64)size)) { + res = -errno; + if (old_ea_info) + restore_ea_info(nai, + old_ea_info); + } + } + ntfs_attr_close(na); + } + ntfs_attr_close(nai); + } else { + res = -errno; + } + return (res); +} + +/* + * Return the existing EA + * + * The EA_INFORMATION is not examined and the consistency of the + * existing EA is not checked. + * + * If successful, the full attribute is returned unchanged + * and its size is returned. + * If the designated buffer is too small, the needed size is + * returned, and the buffer is left unchanged. + * If there is an error, a negative value is returned and errno + * is set according to the error. + */ + +int ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size) +{ + s64 ea_size; + void *ea_buf; + int res = 0; + + if (ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) { + ea_buf = ntfs_attr_readall(ni, AT_EA, (ntfschar*)NULL, 0, + &ea_size); + if (ea_buf) { + if (value && (ea_size <= (s64)size)) + memcpy(value, ea_buf, ea_size); + free(ea_buf); + res = ea_size; + } else { + ntfs_log_error("Failed to read EA from inode %lld\n", + (long long)ni->mft_no); + errno = ENODATA; + res = -errno; + } + } else { + errno = ENODATA; + res = -errno; + } + return (res); +} + +/* + * Set a new EA, and set EA_INFORMATION accordingly + * + * This is roughly the same as ZwSetEaFile() on Windows, however + * the "offset to next" of the last EA should not be cleared. + * + * Consistency of the new EA is first checked. + * + * EA_INFORMATION is set first, and it is restored to its former + * state if setting EA fails. + * + * Returns 0 if successful + * a negative value if an error occurred. + */ + +int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags) +{ + EA_INFORMATION ea_info; + EA_INFORMATION *old_ea_info; + s64 old_ea_size; + int res; + size_t offs; + size_t nextoffs; + BOOL ok; + int ea_count; + int ea_packed; + const EA_ATTR *p_ea; + + res = -1; + if (value && (size > 0)) { + /* do consistency checks */ + offs = 0; + ok = TRUE; + ea_count = 0; + ea_packed = 0; + nextoffs = 0; + while (ok && (offs < size)) { + p_ea = (const EA_ATTR*)&value[offs]; + nextoffs = offs + le32_to_cpu(p_ea->next_entry_offset); + /* null offset to next not allowed */ + ok = (nextoffs > offs) + && (nextoffs <= size) + && !(nextoffs & 3) + && p_ea->name_length + /* zero sized value are allowed */ + && ((offs + offsetof(EA_ATTR,name) + + p_ea->name_length + 1 + + le16_to_cpu(p_ea->value_length)) + <= nextoffs) + && ((offs + offsetof(EA_ATTR,name) + + p_ea->name_length + 1 + + le16_to_cpu(p_ea->value_length)) + >= (nextoffs - 3)) + && !p_ea->name[p_ea->name_length]; + /* name not checked, as chkdsk accepts any chars */ + if (ok) { + if (p_ea->flags & NEED_EA) + ea_count++; + /* + * Assume ea_packed includes : + * 4 bytes for header (flags and lengths) + * + name length + 1 + * + value length + */ + ea_packed += 5 + p_ea->name_length + + le16_to_cpu(p_ea->value_length); + offs = nextoffs; + } + } + /* + * EA and REPARSE_POINT exclude each other + * see http://msdn.microsoft.com/en-us/library/windows/desktop/aa364404(v=vs.85).aspx + * Also return EINVAL if REPARSE_POINT is present. + */ + if (ok + && !ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED,0)) { + ea_info.ea_length = cpu_to_le16(ea_packed); + ea_info.need_ea_count = cpu_to_le16(ea_count); + ea_info.ea_query_length = cpu_to_le32(nextoffs); + + old_ea_size = 0; + old_ea_info = NULL; + /* Try to save the old EA_INFORMATION */ + if (ntfs_attr_exist(ni, AT_EA_INFORMATION, + AT_UNNAMED, 0)) { + old_ea_info = ntfs_attr_readall(ni, + AT_EA_INFORMATION, + (ntfschar*)NULL, 0, &old_ea_size); + } + /* + * no EA or EA_INFORMATION : add them + */ + if (!ntfs_need_ea(ni, AT_EA_INFORMATION, + sizeof(EA_INFORMATION), flags) + && !ntfs_need_ea(ni, AT_EA, 0, flags)) { + res = ntfs_update_ea(ni, value, size, + &ea_info, old_ea_info); + } else { + res = -errno; + } + if (old_ea_info) + free(old_ea_info); + } else { + errno = EINVAL; + res = -errno; + } + } else { + errno = EINVAL; + res = -errno; + } + return (res); +} + +/* + * Remove the EA (including EA_INFORMATION) + * + * EA_INFORMATION is removed first, and it is restored to its former + * state if removing EA fails. + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_remove_ntfs_ea(ntfs_inode *ni) +{ + EA_INFORMATION *old_ea_info; + s64 old_ea_size; + int res; + ntfs_attr *na; + ntfs_attr *nai; + + res = 0; + if (ni) { + /* + * open and delete the EA_INFORMATION and the EA + */ + nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0); + if (nai) { + na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0); + if (na) { + /* Try to save the old EA_INFORMATION */ + old_ea_info = ntfs_attr_readall(ni, + AT_EA_INFORMATION, + (ntfschar*)NULL, 0, &old_ea_size); + res = ntfs_attr_rm(na); + NInoFileNameSetDirty(ni); + if (!res) { + res = ntfs_attr_rm(nai); + if (res && old_ea_info) { + /* + * Failed to remove the EA, try to + * restore the EA_INFORMATION + */ + restore_ea_info(nai, + old_ea_info); + } + } else { + ntfs_log_error("Failed to remove the" + " EA_INFORMATION from inode %lld\n", + (long long)ni->mft_no); + } + free(old_ea_info); + ntfs_attr_close(na); + } else { + /* EA_INFORMATION present, but no EA */ + res = ntfs_attr_rm(nai); + NInoFileNameSetDirty(ni); + } + ntfs_attr_close(nai); + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ diff --git a/libntfs-3g/efs.c b/libntfs-3g/efs.c new file mode 100755 index 0000000000000000000000000000000000000000..7957005b6b629d619b3ecdf5ac69cbdb749d710f --- /dev/null +++ b/libntfs-3g/efs.c @@ -0,0 +1,437 @@ +/** + * efs.c - Limited processing of encrypted files + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2009 Martin Bene + * Copyright (c) 2009-2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include <sys/sysmacros.h> +#endif + +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "efs.h" +#include "index.h" +#include "logging.h" +#include "misc.h" +#include "efs.h" + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +static ntfschar logged_utility_stream_name[] = { + const_cpu_to_le16('$'), + const_cpu_to_le16('E'), + const_cpu_to_le16('F'), + const_cpu_to_le16('S'), + const_cpu_to_le16(0) +} ; + + +/* + * Get the ntfs EFS info into an extended attribute + */ + +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size) +{ + EFS_ATTR_HEADER *efs_info; + s64 attr_size = 0; + + if (ni) { + if (ni->flags & FILE_ATTR_ENCRYPTED) { + efs_info = (EFS_ATTR_HEADER*)ntfs_attr_readall(ni, + AT_LOGGED_UTILITY_STREAM,(ntfschar*)NULL, 0, + &attr_size); + if (efs_info + && (le32_to_cpu(efs_info->length) == attr_size)) { + if (attr_size <= (s64)size) { + if (value) + memcpy(value,efs_info,attr_size); + else { + errno = EFAULT; + attr_size = 0; + } + } else + if (size) { + errno = ERANGE; + attr_size = 0; + } + free (efs_info); + } else { + if (efs_info) { + free(efs_info); + ntfs_log_error("Bad efs_info for inode %lld\n", + (long long)ni->mft_no); + } else { + ntfs_log_error("Could not get efsinfo" + " for inode %lld\n", + (long long)ni->mft_no); + } + errno = EIO; + attr_size = 0; + } + } else { + errno = ENODATA; + ntfs_log_trace("Inode %lld is not encrypted\n", + (long long)ni->mft_no); + } + } + return (attr_size ? (int)attr_size : -errno); +} + +/* + * Fix all encrypted AT_DATA attributes of an inode + * + * The fix may require making an attribute non resident, which + * requires more space in the MFT record, and may cause some + * attribute to be expelled and the full record to be reorganized. + * When this happens, the search for data attributes has to be + * reinitialized. + * + * Returns zero if successful. + * -1 if there is a problem. + */ + +static int fixup_loop(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ctx; + ntfs_attr *na; + ATTR_RECORD *a; + BOOL restart; + int cnt; + int maxcnt; + int res = 0; + + maxcnt = 0; + do { + restart = FALSE; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + res = -1; + } + cnt = 0; + while (!restart && !res + && !ntfs_attr_lookup(AT_DATA, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + cnt++; + a = ctx->attr; + na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA, + (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), + a->name_length); + if (!na) { + ntfs_log_error("can't open DATA Attribute\n"); + res = -1; + } + if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) { + if (!NAttrNonResident(na) + && ntfs_attr_make_non_resident(na, ctx)) { + /* + * ntfs_attr_make_non_resident fails if there + * is not enough space in the MFT record. + * When this happens, force making non-resident + * so that some other attribute is expelled. + */ + if (ntfs_attr_force_non_resident(na)) { + res = -1; + } else { + /* make sure there is some progress */ + if (cnt <= maxcnt) { + errno = EIO; + ntfs_log_error("Multiple failure" + " making non resident\n"); + res = -1; + } else { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + restart = TRUE; + maxcnt = cnt; + } + } + } + if (!restart && !res + && ntfs_efs_fixup_attribute(ctx, na)) { + ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n"); + res = -1; + } + } + if (na) + ntfs_attr_close(na); + } + } while (restart && !res); + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return (res); +} + +/* + * Set the efs data from an extended attribute + * Warning : the new data is not checked + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size, + int flags) + +{ + int res; + int written; + ntfs_attr *na; + const EFS_ATTR_HEADER *info_header; + + res = 0; + if (ni && value && size) { + if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) { + if (ni->flags & FILE_ATTR_ENCRYPTED) { + ntfs_log_trace("Inode %lld already encrypted\n", + (long long)ni->mft_no); + errno = EEXIST; + } else { + /* + * Possible problem : if encrypted file was + * restored in a compressed directory, it was + * restored as compressed. + * TODO : decompress first. + */ + ntfs_log_error("Inode %lld cannot be encrypted and compressed\n", + (long long)ni->mft_no); + errno = EIO; + } + return -1; + } + info_header = (const EFS_ATTR_HEADER*)value; + /* make sure we get a likely efsinfo */ + if (le32_to_cpu(info_header->length) != size) { + errno = EINVAL; + return (-1); + } + if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM, + (ntfschar*)NULL,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no logged_utility_stream attribute : add one, + * apparently, this does not feed the new value in + */ + res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM, + logged_utility_stream_name,4, + (u8*)NULL,(s64)size); + } else { + errno = ENODATA; + res = -1; + } + } else { + errno = EEXIST; + res = -1; + } + if (!res) { + /* + * open and update the existing efs data + */ + na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, + logged_utility_stream_name, 4); + if (na) { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)size); + /* overwrite value if any */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)size, value); + if (written != (s64)size) { + ntfs_log_error("Failed to " + "update efs data\n"); + errno = EIO; + res = -1; + } + } + ntfs_attr_close(na); + } else + res = -1; + } + if (!res) { + /* Don't handle AT_DATA Attribute(s) if inode is a directory */ + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + /* iterate over AT_DATA attributes */ + /* set encrypted flag, truncate attribute to match padding bytes */ + + if (fixup_loop(ni)) + return -1; + } + ni->flags |= FILE_ATTR_ENCRYPTED; + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Fixup raw encrypted AT_DATA Attribute + * read padding length from last two bytes + * truncate attribute, make non-resident, + * set data size to match padding length + * set ATTR_IS_ENCRYPTED flag on attribute + * + * Return 0 if successful + * -1 if failed (errno tells why) + */ + +int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) +{ + u64 newsize; + u64 oldsize; + le16 appended_bytes; + u16 padding_length; + ntfs_inode *ni; + BOOL close_ctx = FALSE; + + if (!na) { + ntfs_log_error("no na specified for efs_fixup_attribute\n"); + goto err_out; + } + if (!ctx) { + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + goto err_out; + } + close_ctx = TRUE; + if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); + goto err_out; + } + } else { + if (!NAttrNonResident(na)) { + ntfs_log_error("Cannot make non resident" + " when a context has been allocated\n"); + goto err_out; + } + } + + /* no extra bytes are added to void attributes */ + oldsize = na->data_size; + if (oldsize) { + /* make sure size is valid for a raw encrypted stream */ + if ((oldsize & 511) != 2) { + ntfs_log_error("Bad raw encrypted stream\n"); + goto err_out; + } + /* read padding length from last two bytes of attribute */ + if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2) { + ntfs_log_error("Error reading padding length\n"); + goto err_out; + } + padding_length = le16_to_cpu(appended_bytes); + if (padding_length > 511 || padding_length > na->data_size-2) { + errno = EINVAL; + ntfs_log_error("invalid padding length %d for data_size %lld\n", + padding_length, (long long)oldsize); + goto err_out; + } + newsize = oldsize - padding_length - 2; + /* + * truncate attribute to possibly free clusters allocated + * for the last two bytes, but do not truncate to new size + * to avoid losing useful data + */ + if (ntfs_attr_truncate(na, oldsize - 2)) { + ntfs_log_error("Error truncating attribute\n"); + goto err_out; + } + } else + newsize = 0; + + /* + * Encrypted AT_DATA Attributes MUST be non-resident + * This has to be done after the attribute is resized, as + * resizing down to zero may cause the attribute to be made + * resident. + */ + if (!NAttrNonResident(na) + && ntfs_attr_make_non_resident(na, ctx)) { + if (!close_ctx + || ntfs_attr_force_non_resident(na)) { + ntfs_log_error("Error making DATA attribute non-resident\n"); + goto err_out; + } else { + /* + * must reinitialize context after forcing + * non-resident. We need a context for updating + * the state, and at this point, we are sure + * the context is not used elsewhere. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); + goto err_out; + } + } + } + ni = na->ni; + if (!na->name_len) { + ni->data_size = newsize; + ni->allocated_size = na->allocated_size; + } + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + + ctx->attr->data_size = cpu_to_le64(newsize); + if (le64_to_cpu(ctx->attr->initialized_size) > newsize) + ctx->attr->initialized_size = ctx->attr->data_size; + ctx->attr->flags |= ATTR_IS_ENCRYPTED; + if (close_ctx) + ntfs_attr_put_search_ctx(ctx); + + return (0); +err_out: + if (close_ctx && ctx) + ntfs_attr_put_search_ctx(ctx); + return (-1); +} + +#endif /* HAVE_SETXATTR */ diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c new file mode 100755 index 0000000000000000000000000000000000000000..d498dde4b6b4e554ebe76108ee3cf117a7115969 --- /dev/null +++ b/libntfs-3g/index.c @@ -0,0 +1,2085 @@ +/** + * index.c - NTFS index handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2005-2006 Yura Pakhuchiy + * Copyright (c) 2005-2008 Szabolcs Szakacsits + * Copyright (c) 2007 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "attrib.h" +#include "debug.h" +#include "index.h" +#include "collate.h" +#include "mst.h" +#include "dir.h" +#include "logging.h" +#include "bitmap.h" +#include "reparse.h" +#include "misc.h" + +/** + * ntfs_index_entry_mark_dirty - mark an index entry dirty + * @ictx: ntfs index context describing the index entry + * + * Mark the index entry described by the index entry context @ictx dirty. + * + * If the index entry is in the index root attribute, simply mark the inode + * containing the index root attribute dirty. This ensures the mftrecord, and + * hence the index root attribute, will be written out to disk later. + * + * If the index entry is in an index block belonging to the index allocation + * attribute, set ib_dirty to TRUE, thus index block will be updated during + * ntfs_index_ctx_put. + */ +void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx) +{ + if (ictx->is_in_root) + ntfs_inode_mark_dirty(ictx->actx->ntfs_ino); + else + ictx->ib_dirty = TRUE; +} + +static s64 ntfs_ib_vcn_to_pos(ntfs_index_context *icx, VCN vcn) +{ + return vcn << icx->vcn_size_bits; +} + +static VCN ntfs_ib_pos_to_vcn(ntfs_index_context *icx, s64 pos) +{ + return pos >> icx->vcn_size_bits; +} + +static int ntfs_ib_write(ntfs_index_context *icx, INDEX_BLOCK *ib) +{ + s64 ret, vcn = sle64_to_cpu(ib->index_block_vcn); + + ntfs_log_trace("vcn: %lld\n", (long long)vcn); + + ret = ntfs_attr_mst_pwrite(icx->ia_na, ntfs_ib_vcn_to_pos(icx, vcn), + 1, icx->block_size, ib); + if (ret != 1) { + ntfs_log_perror("Failed to write index block %lld, inode %llu", + (long long)vcn, (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } + + return STATUS_OK; +} + +static int ntfs_icx_ib_write(ntfs_index_context *icx) +{ + if (ntfs_ib_write(icx, icx->ib)) + return STATUS_ERROR; + + icx->ib_dirty = FALSE; + + return STATUS_OK; +} + +/** + * ntfs_index_ctx_get - allocate and initialize a new index context + * @ni: ntfs inode with which to initialize the context + * @name: name of the which context describes + * @name_len: length of the index name + * + * Allocate a new index context, initialize it with @ni and return it. + * Return NULL if allocation failed. + */ +ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, + ntfschar *name, u32 name_len) +{ + ntfs_index_context *icx; + + ntfs_log_trace("Entering\n"); + + if (!ni) { + errno = EINVAL; + return NULL; + } + if (ni->nr_extents == -1) + ni = ni->base_ni; + icx = ntfs_calloc(sizeof(ntfs_index_context)); + if (icx) + *icx = (ntfs_index_context) { + .ni = ni, + .name = name, + .name_len = name_len, + }; + return icx; +} + +static void ntfs_index_ctx_free(ntfs_index_context *icx) +{ + ntfs_log_trace("Entering\n"); + + if (!icx->entry) + return; + + if (icx->actx) + ntfs_attr_put_search_ctx(icx->actx); + + if (!icx->is_in_root) { + if (icx->ib_dirty) { + /* FIXME: Error handling!!! */ + ntfs_ib_write(icx, icx->ib); + } + free(icx->ib); + } + + ntfs_attr_close(icx->ia_na); +} + +/** + * ntfs_index_ctx_put - release an index context + * @icx: index context to free + * + * Release the index context @icx, releasing all associated resources. + */ +void ntfs_index_ctx_put(ntfs_index_context *icx) +{ + ntfs_index_ctx_free(icx); + free(icx); +} + +/** + * ntfs_index_ctx_reinit - reinitialize an index context + * @icx: index context to reinitialize + * + * Reinitialize the index context @icx so it can be used for ntfs_index_lookup. + */ +void ntfs_index_ctx_reinit(ntfs_index_context *icx) +{ + ntfs_log_trace("Entering\n"); + + ntfs_index_ctx_free(icx); + + *icx = (ntfs_index_context) { + .ni = icx->ni, + .name = icx->name, + .name_len = icx->name_len, + }; +} + +static VCN *ntfs_ie_get_vcn_addr(INDEX_ENTRY *ie) +{ + return (VCN *)((u8 *)ie + le16_to_cpu(ie->length) - sizeof(VCN)); +} + +/** + * Get the subnode vcn to which the index entry refers. + */ +VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie) +{ + return sle64_to_cpup(ntfs_ie_get_vcn_addr(ie)); +} + +static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih) +{ + return (INDEX_ENTRY *)((u8 *)ih + le32_to_cpu(ih->entries_offset)); +} + +static INDEX_ENTRY *ntfs_ie_get_next(INDEX_ENTRY *ie) +{ + return (INDEX_ENTRY *)((char *)ie + le16_to_cpu(ie->length)); +} + +static u8 *ntfs_ie_get_end(INDEX_HEADER *ih) +{ + /* FIXME: check if it isn't overflowing the index block size */ + return (u8 *)ih + le32_to_cpu(ih->index_length); +} + +static int ntfs_ie_end(INDEX_ENTRY *ie) +{ + return ie->ie_flags & INDEX_ENTRY_END || !ie->length; +} + +/** + * Find the last entry in the index block + */ +static INDEX_ENTRY *ntfs_ie_get_last(INDEX_ENTRY *ie, char *ies_end) +{ + ntfs_log_trace("Entering\n"); + + while ((char *)ie < ies_end && !ntfs_ie_end(ie)) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static INDEX_ENTRY *ntfs_ie_get_by_pos(INDEX_HEADER *ih, int pos) +{ + INDEX_ENTRY *ie; + + ntfs_log_trace("pos: %d\n", pos); + + ie = ntfs_ie_get_first(ih); + + while (pos-- > 0) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static INDEX_ENTRY *ntfs_ie_prev(INDEX_HEADER *ih, INDEX_ENTRY *ie) +{ + INDEX_ENTRY *ie_prev = NULL; + INDEX_ENTRY *tmp; + + ntfs_log_trace("Entering\n"); + + tmp = ntfs_ie_get_first(ih); + + while (tmp != ie) { + ie_prev = tmp; + tmp = ntfs_ie_get_next(tmp); + } + + return ie_prev; +} + +char *ntfs_ie_filename_get(INDEX_ENTRY *ie) +{ + FILE_NAME_ATTR *fn; + + fn = (FILE_NAME_ATTR *)&ie->key; + return ntfs_attr_name_get(fn->file_name, fn->file_name_length); +} + +void ntfs_ie_filename_dump(INDEX_ENTRY *ie) +{ + char *s; + + s = ntfs_ie_filename_get(ie); + ntfs_log_debug("'%s' ", s); + ntfs_attr_name_free(&s); +} + +void ntfs_ih_filename_dump(INDEX_HEADER *ih) +{ + INDEX_ENTRY *ie; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_first(ih); + while (!ntfs_ie_end(ie)) { + ntfs_ie_filename_dump(ie); + ie = ntfs_ie_get_next(ie); + } +} + +static int ntfs_ih_numof_entries(INDEX_HEADER *ih) +{ + int n; + INDEX_ENTRY *ie; + u8 *end; + + ntfs_log_trace("Entering\n"); + + end = ntfs_ie_get_end(ih); + ie = ntfs_ie_get_first(ih); + for (n = 0; !ntfs_ie_end(ie) && (u8 *)ie < end; n++) + ie = ntfs_ie_get_next(ie); + return n; +} + +static int ntfs_ih_one_entry(INDEX_HEADER *ih) +{ + return (ntfs_ih_numof_entries(ih) == 1); +} + +static int ntfs_ih_zero_entry(INDEX_HEADER *ih) +{ + return (ntfs_ih_numof_entries(ih) == 0); +} + +static void ntfs_ie_delete(INDEX_HEADER *ih, INDEX_ENTRY *ie) +{ + u32 new_size; + + ntfs_log_trace("Entering\n"); + + new_size = le32_to_cpu(ih->index_length) - le16_to_cpu(ie->length); + ih->index_length = cpu_to_le32(new_size); + memmove(ie, (u8 *)ie + le16_to_cpu(ie->length), + new_size - ((u8 *)ie - (u8 *)ih)); +} + +static void ntfs_ie_set_vcn(INDEX_ENTRY *ie, VCN vcn) +{ + *ntfs_ie_get_vcn_addr(ie) = cpu_to_le64(vcn); +} + +/** + * Insert @ie index entry at @pos entry. Used @ih values should be ok already. + */ +static void ntfs_ie_insert(INDEX_HEADER *ih, INDEX_ENTRY *ie, INDEX_ENTRY *pos) +{ + int ie_size = le16_to_cpu(ie->length); + + ntfs_log_trace("Entering\n"); + + ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) + ie_size); + memmove((u8 *)pos + ie_size, pos, + le32_to_cpu(ih->index_length) - ((u8 *)pos - (u8 *)ih) - ie_size); + memcpy(pos, ie, ie_size); +} + +static INDEX_ENTRY *ntfs_ie_dup(INDEX_ENTRY *ie) +{ + INDEX_ENTRY *dup; + + ntfs_log_trace("Entering\n"); + + dup = ntfs_malloc(le16_to_cpu(ie->length)); + if (dup) + memcpy(dup, ie, le16_to_cpu(ie->length)); + + return dup; +} + +static INDEX_ENTRY *ntfs_ie_dup_novcn(INDEX_ENTRY *ie) +{ + INDEX_ENTRY *dup; + int size = le16_to_cpu(ie->length); + + ntfs_log_trace("Entering\n"); + + if (ie->ie_flags & INDEX_ENTRY_NODE) + size -= sizeof(VCN); + + dup = ntfs_malloc(size); + if (dup) { + memcpy(dup, ie, size); + dup->ie_flags &= ~INDEX_ENTRY_NODE; + dup->length = cpu_to_le16(size); + } + return dup; +} + +static int ntfs_ia_check(ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn) +{ + u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size) + 0x18; + + ntfs_log_trace("Entering\n"); + + if (!ntfs_is_indx_record(ib->magic)) { + + ntfs_log_error("Corrupt index block signature: vcn %lld inode " + "%llu\n", (long long)vcn, + (unsigned long long)icx->ni->mft_no); + return -1; + } + + if (sle64_to_cpu(ib->index_block_vcn) != vcn) { + + ntfs_log_error("Corrupt index block: VCN (%lld) is different " + "from expected VCN (%lld) in inode %llu\n", + (long long)sle64_to_cpu(ib->index_block_vcn), + (long long)vcn, + (unsigned long long)icx->ni->mft_no); + return -1; + } + + if (ib_size != icx->block_size) { + + ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu " + "has a size (%u) differing from the index " + "specified size (%u)\n", (long long)vcn, + (unsigned long long)icx->ni->mft_no, ib_size, + icx->block_size); + return -1; + } + return 0; +} + +static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, + u32 name_len, ntfs_attr_search_ctx **ctx) +{ + ATTR_RECORD *a; + INDEX_ROOT *ir = NULL; + + ntfs_log_trace("Entering\n"); + + *ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!*ctx) + return NULL; + + if (ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE, + 0, NULL, 0, *ctx)) { + ntfs_log_perror("Failed to lookup $INDEX_ROOT"); + goto err_out; + } + + a = (*ctx)->attr; + if (a->non_resident) { + errno = EINVAL; + ntfs_log_perror("Non-resident $INDEX_ROOT detected"); + goto err_out; + } + + ir = (INDEX_ROOT *)((char *)a + le16_to_cpu(a->value_offset)); +err_out: + if (!ir) { + ntfs_attr_put_search_ctx(*ctx); + *ctx = NULL; + } + return ir; +} + +static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len) +{ + ntfs_attr_search_ctx *ctx; + INDEX_ROOT *ir; + + ir = ntfs_ir_lookup(ni, name, len, &ctx); + if (ir) + ntfs_attr_put_search_ctx(ctx); + return ir; +} + +/** + * Find a key in the index block. + * + * Return values: + * STATUS_OK with errno set to ESUCCESS if we know for sure that the + * entry exists and @ie_out points to this entry. + * STATUS_NOT_FOUND with errno set to ENOENT if we know for sure the + * entry doesn't exist and @ie_out is the insertion point. + * STATUS_KEEP_SEARCHING if we can't answer the above question and + * @vcn will contain the node index block. + * STATUS_ERROR with errno set if on unexpected error during lookup. + */ +static int ntfs_ie_lookup(const void *key, const int key_len, + ntfs_index_context *icx, INDEX_HEADER *ih, + VCN *vcn, INDEX_ENTRY **ie_out) +{ + INDEX_ENTRY *ie; + u8 *index_end; + int rc, item = 0; + + ntfs_log_trace("Entering\n"); + + index_end = ntfs_ie_get_end(ih); + + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + for (ie = ntfs_ie_get_first(ih); ; ie = ntfs_ie_get_next(ie)) { + /* Bounds checks. */ + if ((u8 *)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || + (u8 *)ie + le16_to_cpu(ie->length) > index_end) { + errno = ERANGE; + ntfs_log_error("Index entry out of bounds in inode " + "%llu.\n", + (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } + /* + * The last entry cannot contain a key. It can however contain + * a pointer to a child node in the B+tree so we just break out. + */ + if (ntfs_ie_end(ie)) + break; + /* + * Not a perfect match, need to do full blown collation so we + * know which way in the B+tree we have to go. + */ + if (!icx->collate) { + ntfs_log_error("Collation function not defined\n"); + errno = EOPNOTSUPP; + return STATUS_ERROR; + } + rc = icx->collate(icx->ni->vol, key, key_len, + &ie->key, le16_to_cpu(ie->key_length)); + if (rc == NTFS_COLLATION_ERROR) { + ntfs_log_error("Collation error. Perhaps a filename " + "contains invalid characters?\n"); + errno = ERANGE; + return STATUS_ERROR; + } + /* + * If @key collates before the key of the current entry, there + * is definitely no such key in this index but we might need to + * descend into the B+tree so we just break out of the loop. + */ + if (rc == -1) + break; + + if (!rc) { + *ie_out = ie; + errno = 0; + icx->parent_pos[icx->pindex] = item; + return STATUS_OK; + } + + item++; + } + /* + * We have finished with this index block without success. Check for the + * presence of a child node and if not present return with errno ENOENT, + * otherwise we will keep searching in another index block. + */ + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) { + ntfs_log_debug("Index entry wasn't found.\n"); + *ie_out = ie; + errno = ENOENT; + return STATUS_NOT_FOUND; + } + + /* Get the starting vcn of the index_block holding the child node. */ + *vcn = ntfs_ie_get_vcn(ie); + if (*vcn < 0) { + errno = EINVAL; + ntfs_log_perror("Negative vcn in inode %llu", + (unsigned long long)icx->ni->mft_no); + return STATUS_ERROR; + } + + ntfs_log_trace("Parent entry number %d\n", item); + icx->parent_pos[icx->pindex] = item; + + return STATUS_KEEP_SEARCHING; +} + +static ntfs_attr *ntfs_ia_open(ntfs_index_context *icx, ntfs_inode *ni) +{ + ntfs_attr *na; + + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open index allocation of inode " + "%llu", (unsigned long long)ni->mft_no); + return NULL; + } + + return na; +} + +static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst) +{ + s64 pos, ret; + + ntfs_log_trace("vcn: %lld\n", (long long)vcn); + + pos = ntfs_ib_vcn_to_pos(icx, vcn); + + ret = ntfs_attr_mst_pread(icx->ia_na, pos, 1, icx->block_size, (u8 *)dst); + if (ret != 1) { + if (ret == -1) + ntfs_log_perror("Failed to read index block"); + else + ntfs_log_error("Failed to read full index block at " + "%lld\n", (long long)pos); + return -1; + } + + if (ntfs_ia_check(icx, dst, vcn)) + return -1; + + return 0; +} + +static int ntfs_icx_parent_inc(ntfs_index_context *icx) +{ + icx->pindex++; + if (icx->pindex >= MAX_PARENT_VCN) { + errno = EOPNOTSUPP; + ntfs_log_perror("Index is over %d level deep", MAX_PARENT_VCN); + return STATUS_ERROR; + } + return STATUS_OK; +} + +static int ntfs_icx_parent_dec(ntfs_index_context *icx) +{ + icx->pindex--; + if (icx->pindex < 0) { + errno = EINVAL; + ntfs_log_perror("Corrupt index pointer (%d)", icx->pindex); + return STATUS_ERROR; + } + return STATUS_OK; +} + +/** + * ntfs_index_lookup - find a key in an index and return its index entry + * @key: [IN] key for which to search in the index + * @key_len: [IN] length of @key in bytes + * @icx: [IN/OUT] context describing the index and the returned entry + * + * Before calling ntfs_index_lookup(), @icx must have been obtained from a + * call to ntfs_index_ctx_get(). + * + * Look for the @key in the index specified by the index lookup context @icx. + * ntfs_index_lookup() walks the contents of the index looking for the @key. + * + * If the @key is found in the index, 0 is returned and @icx is setup to + * describe the index entry containing the matching @key. @icx->entry is the + * index entry and @icx->data and @icx->data_len are the index entry data and + * its length in bytes, respectively. + * + * If the @key is not found in the index, -1 is returned, errno = ENOENT and + * @icx is setup to describe the index entry whose key collates immediately + * after the search @key, i.e. this is the position in the index at which + * an index entry with a key of @key would need to be inserted. + * + * If an error occurs return -1, set errno to error code and @icx is left + * untouched. + * + * When finished with the entry and its data, call ntfs_index_ctx_put() to free + * the context and other associated resources. + * + * If the index entry was modified, call ntfs_index_entry_mark_dirty() before + * the call to ntfs_index_ctx_put() to ensure that the changes are written + * to disk. + */ +int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *icx) +{ + VCN old_vcn, vcn; + ntfs_inode *ni = icx->ni; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_BLOCK *ib = NULL; + int ret, err = 0; + + ntfs_log_trace("Entering\n"); + + if (!key || key_len <= 0) { + errno = EINVAL; + ntfs_log_perror("key: %p key_len: %d", key, key_len); + return -1; + } + + ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &icx->actx); + if (!ir) { + if (errno == ENOENT) + errno = EIO; + return -1; + } + + icx->block_size = le32_to_cpu(ir->index_block_size); + if (icx->block_size < NTFS_BLOCK_SIZE) { + errno = EINVAL; + ntfs_log_perror("Index block size (%d) is smaller than the " + "sector size (%d)", icx->block_size, NTFS_BLOCK_SIZE); + goto err_out; + } + + if (ni->vol->cluster_size <= icx->block_size) + icx->vcn_size_bits = ni->vol->cluster_size_bits; + else + icx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS; + /* get the appropriate collation function */ + icx->collate = ntfs_get_collate_function(ir->collation_rule); + if (!icx->collate) { + err = errno = EOPNOTSUPP; + ntfs_log_perror("Unknown collation rule 0x%x", + (unsigned)le32_to_cpu(ir->collation_rule)); + goto err_out; + } + + old_vcn = VCN_INDEX_ROOT_PARENT; + /* + * FIXME: check for both ir and ib that the first index entry is + * within the index block. + */ + ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); + if (ret == STATUS_ERROR) { + err = errno; + goto err_out; + } + + icx->ir = ir; + + if (ret != STATUS_KEEP_SEARCHING) { + /* STATUS_OK or STATUS_NOT_FOUND */ + err = errno; + icx->is_in_root = TRUE; + icx->parent_vcn[icx->pindex] = old_vcn; + goto done; + } + + /* Child node present, descend into it. */ + + icx->ia_na = ntfs_ia_open(icx, ni); + if (!icx->ia_na) + goto err_out; + + ib = ntfs_malloc(icx->block_size); + if (!ib) { + err = errno; + goto err_out; + } + +descend_into_child_node: + + icx->parent_vcn[icx->pindex] = old_vcn; + if (ntfs_icx_parent_inc(icx)) { + err = errno; + goto err_out; + } + old_vcn = vcn; + + ntfs_log_debug("Descend into node with VCN %lld\n", (long long)vcn); + + if (ntfs_ib_read(icx, vcn, ib)) + goto err_out; + + ret = ntfs_ie_lookup(key, key_len, icx, &ib->index, &vcn, &ie); + if (ret != STATUS_KEEP_SEARCHING) { + err = errno; + if (ret == STATUS_ERROR) + goto err_out; + + /* STATUS_OK or STATUS_NOT_FOUND */ + icx->is_in_root = FALSE; + icx->ib = ib; + icx->parent_vcn[icx->pindex] = vcn; + goto done; + } + + if ((ib->index.ih_flags & NODE_MASK) == LEAF_NODE) { + ntfs_log_error("Index entry with child node found in a leaf " + "node in inode 0x%llx.\n", + (unsigned long long)ni->mft_no); + goto err_out; + } + + goto descend_into_child_node; +err_out: + free(ib); + if (!err) + err = EIO; + errno = err; + return -1; +done: + icx->entry = ie; + icx->data = (u8 *)ie + offsetof(INDEX_ENTRY, key); + icx->data_len = le16_to_cpu(ie->key_length); + ntfs_log_trace("Done.\n"); + if (err) { + errno = err; + return -1; + } + return 0; + +} + +static INDEX_BLOCK *ntfs_ib_alloc(VCN ib_vcn, u32 ib_size, + INDEX_HEADER_FLAGS node_type) +{ + INDEX_BLOCK *ib; + int ih_size = sizeof(INDEX_HEADER); + + ntfs_log_trace("ib_vcn: %lld ib_size: %u\n", (long long)ib_vcn, ib_size); + + ib = ntfs_calloc(ib_size); + if (!ib) + return NULL; + + ib->magic = magic_INDX; + ib->usa_ofs = cpu_to_le16(sizeof(INDEX_BLOCK)); + ib->usa_count = cpu_to_le16(ib_size / NTFS_BLOCK_SIZE + 1); + /* Set USN to 1 */ + *(u16 *)((char *)ib + le16_to_cpu(ib->usa_ofs)) = cpu_to_le16(1); + ib->lsn = cpu_to_le64(0); + + ib->index_block_vcn = cpu_to_sle64(ib_vcn); + + ib->index.entries_offset = cpu_to_le32((ih_size + + le16_to_cpu(ib->usa_count) * 2 + 7) & ~7); + ib->index.index_length = 0; + ib->index.allocated_size = cpu_to_le32(ib_size - + (sizeof(INDEX_BLOCK) - ih_size)); + ib->index.ih_flags = node_type; + + return ib; +} + +/** + * Find the median by going through all the entries + */ +static INDEX_ENTRY *ntfs_ie_get_median(INDEX_HEADER *ih) +{ + INDEX_ENTRY *ie, *ie_start; + u8 *ie_end; + int i = 0, median; + + ntfs_log_trace("Entering\n"); + + ie = ie_start = ntfs_ie_get_first(ih); + ie_end = (u8 *)ntfs_ie_get_end(ih); + + while ((u8 *)ie < ie_end && !ntfs_ie_end(ie)) { + ie = ntfs_ie_get_next(ie); + i++; + } + /* + * NOTE: this could be also the entry at the half of the index block. + */ + median = i / 2 - 1; + + ntfs_log_trace("Entries: %d median: %d\n", i, median); + + for (i = 0, ie = ie_start; i <= median; i++) + ie = ntfs_ie_get_next(ie); + + return ie; +} + +static s64 ntfs_ibm_vcn_to_pos(ntfs_index_context *icx, VCN vcn) +{ + return ntfs_ib_vcn_to_pos(icx, vcn) / icx->block_size; +} + +static s64 ntfs_ibm_pos_to_vcn(ntfs_index_context *icx, s64 pos) +{ + return ntfs_ib_pos_to_vcn(icx, pos * icx->block_size); +} + +static int ntfs_ibm_add(ntfs_index_context *icx) +{ + u8 bmp[8]; + + ntfs_log_trace("Entering\n"); + + if (ntfs_attr_exist(icx->ni, AT_BITMAP, icx->name, icx->name_len)) + return STATUS_OK; + /* + * AT_BITMAP must be at least 8 bytes. + */ + memset(bmp, 0, sizeof(bmp)); + if (ntfs_attr_add(icx->ni, AT_BITMAP, icx->name, icx->name_len, + bmp, sizeof(bmp))) { + ntfs_log_perror("Failed to add AT_BITMAP"); + return STATUS_ERROR; + } + + return STATUS_OK; +} + +static int ntfs_ibm_modify(ntfs_index_context *icx, VCN vcn, int set) +{ + u8 byte; + s64 pos = ntfs_ibm_vcn_to_pos(icx, vcn); + u32 bpos = pos / 8; + u32 bit = 1 << (pos % 8); + ntfs_attr *na; + int ret = STATUS_ERROR; + + ntfs_log_trace("%s vcn: %lld\n", set ? "set" : "clear", (long long)vcn); + + na = ntfs_attr_open(icx->ni, AT_BITMAP, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open $BITMAP attribute"); + return -1; + } + + if (set) { + if (na->data_size < bpos + 1) { + if (ntfs_attr_truncate(na, (na->data_size + 8) & ~7)) { + ntfs_log_perror("Failed to truncate AT_BITMAP"); + goto err_na; + } + } + } + + if (ntfs_attr_pread(na, bpos, 1, &byte) != 1) { + ntfs_log_perror("Failed to read $BITMAP"); + goto err_na; + } + + if (set) + byte |= bit; + else + byte &= ~bit; + + if (ntfs_attr_pwrite(na, bpos, 1, &byte) != 1) { + ntfs_log_perror("Failed to write $Bitmap"); + goto err_na; + } + + ret = STATUS_OK; +err_na: + ntfs_attr_close(na); + return ret; +} + + +static int ntfs_ibm_set(ntfs_index_context *icx, VCN vcn) +{ + return ntfs_ibm_modify(icx, vcn, 1); +} + +static int ntfs_ibm_clear(ntfs_index_context *icx, VCN vcn) +{ + return ntfs_ibm_modify(icx, vcn, 0); +} + +static VCN ntfs_ibm_get_free(ntfs_index_context *icx) +{ + u8 *bm; + int bit; + s64 vcn, byte, size; + + ntfs_log_trace("Entering\n"); + + bm = ntfs_attr_readall(icx->ni, AT_BITMAP, icx->name, icx->name_len, + &size); + if (!bm) + return (VCN)-1; + + for (byte = 0; byte < size; byte++) { + + if (bm[byte] == 255) + continue; + + for (bit = 0; bit < 8; bit++) { + if (!(bm[byte] & (1 << bit))) { + vcn = ntfs_ibm_pos_to_vcn(icx, byte * 8 + bit); + goto out; + } + } + } + + vcn = ntfs_ibm_pos_to_vcn(icx, size * 8); +out: + ntfs_log_trace("allocated vcn: %lld\n", (long long)vcn); + + if (ntfs_ibm_set(icx, vcn)) + vcn = (VCN)-1; + + free(bm); + return vcn; +} + +static INDEX_BLOCK *ntfs_ir_to_ib(INDEX_ROOT *ir, VCN ib_vcn) +{ + INDEX_BLOCK *ib; + INDEX_ENTRY *ie_last; + char *ies_start, *ies_end; + int i; + + ntfs_log_trace("Entering\n"); + + ib = ntfs_ib_alloc(ib_vcn, le32_to_cpu(ir->index_block_size), LEAF_NODE); + if (!ib) + return NULL; + + ies_start = (char *)ntfs_ie_get_first(&ir->index); + ies_end = (char *)ntfs_ie_get_end(&ir->index); + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + /* + * Copy all entries, including the termination entry + * as well, which can never have any data. + */ + i = (char *)ie_last - ies_start + le16_to_cpu(ie_last->length); + memcpy(ntfs_ie_get_first(&ib->index), ies_start, i); + + ib->index.ih_flags = ir->index.ih_flags; + ib->index.index_length = cpu_to_le32(i + + le32_to_cpu(ib->index.entries_offset)); + return ib; +} + +static void ntfs_ir_nill(INDEX_ROOT *ir) +{ + INDEX_ENTRY *ie_last; + char *ies_start, *ies_end; + + ntfs_log_trace("Entering\n"); + /* + * TODO: This function could be much simpler. + */ + ies_start = (char *)ntfs_ie_get_first(&ir->index); + ies_end = (char *)ntfs_ie_get_end(&ir->index); + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + /* + * Move the index root termination entry forward + */ + if ((char *)ie_last > ies_start) { + memmove(ies_start, (char *)ie_last, le16_to_cpu(ie_last->length)); + ie_last = (INDEX_ENTRY *)ies_start; + } +} + +static int ntfs_ib_copy_tail(ntfs_index_context *icx, INDEX_BLOCK *src, + INDEX_ENTRY *median, VCN new_vcn) +{ + u8 *ies_end; + INDEX_ENTRY *ie_head; /* first entry after the median */ + int tail_size, ret; + INDEX_BLOCK *dst; + + ntfs_log_trace("Entering\n"); + + dst = ntfs_ib_alloc(new_vcn, icx->block_size, + src->index.ih_flags & NODE_MASK); + if (!dst) + return STATUS_ERROR; + + ie_head = ntfs_ie_get_next(median); + + ies_end = (u8 *)ntfs_ie_get_end(&src->index); + tail_size = ies_end - (u8 *)ie_head; + memcpy(ntfs_ie_get_first(&dst->index), ie_head, tail_size); + + dst->index.index_length = cpu_to_le32(tail_size + + le32_to_cpu(dst->index.entries_offset)); + ret = ntfs_ib_write(icx, dst); + + free(dst); + return ret; +} + +static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *ib, + INDEX_ENTRY *ie) +{ + char *ies_start, *ies_end; + INDEX_ENTRY *ie_last; + + ntfs_log_trace("Entering\n"); + + ies_start = (char *)ntfs_ie_get_first(&ib->index); + ies_end = (char *)ntfs_ie_get_end(&ib->index); + + ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); + if (ie_last->ie_flags & INDEX_ENTRY_NODE) + ntfs_ie_set_vcn(ie_last, ntfs_ie_get_vcn(ie)); + + memcpy(ie, ie_last, le16_to_cpu(ie_last->length)); + + ib->index.index_length = cpu_to_le32(((char *)ie - ies_start) + + le16_to_cpu(ie->length) + le32_to_cpu(ib->index.entries_offset)); + + if (ntfs_ib_write(icx, ib)) + return STATUS_ERROR; + + return STATUS_OK; +} + +static int ntfs_ia_add(ntfs_index_context *icx) +{ + ntfs_log_trace("Entering\n"); + + if (ntfs_ibm_add(icx)) + return -1; + + if (!ntfs_attr_exist(icx->ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len)) { + + if (ntfs_attr_add(icx->ni, AT_INDEX_ALLOCATION, icx->name, + icx->name_len, NULL, 0)) { + ntfs_log_perror("Failed to add AT_INDEX_ALLOCATION"); + return -1; + } + } + + icx->ia_na = ntfs_ia_open(icx, icx->ni); + if (!icx->ia_na) + return -1; + + return 0; +} + +static int ntfs_ir_reparent(ntfs_index_context *icx) +{ + ntfs_attr_search_ctx *ctx = NULL; + INDEX_ROOT *ir; + INDEX_ENTRY *ie; + INDEX_BLOCK *ib = NULL; + VCN new_ib_vcn; + int ix_root_size; + int ret = STATUS_ERROR; + + ntfs_log_trace("Entering\n"); + + ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!ir) + goto out; + + if ((ir->index.ih_flags & NODE_MASK) == SMALL_INDEX) + if (ntfs_ia_add(icx)) + goto out; + + new_ib_vcn = ntfs_ibm_get_free(icx); + if (new_ib_vcn == -1) + goto out; + + ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!ir) + goto clear_bmp; + + ib = ntfs_ir_to_ib(ir, new_ib_vcn); + if (ib == NULL) { + ntfs_log_perror("Failed to move index root to index block"); + goto clear_bmp; + } + + if (ntfs_ib_write(icx, ib)) + goto clear_bmp; + +retry : + ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); + if (!ir) + goto clear_bmp; + + ntfs_ir_nill(ir); + + ie = ntfs_ie_get_first(&ir->index); + ie->ie_flags |= INDEX_ENTRY_NODE; + ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)); + + ir->index.ih_flags = LARGE_INDEX; + ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset) + + le16_to_cpu(ie->length)); + ir->index.allocated_size = ir->index.index_length; + ix_root_size = sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + + le32_to_cpu(ir->index.allocated_size); + if (ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, + ix_root_size)) { + /* + * When there is no space to build a non-resident + * index, we may have to move the root to an extent + */ + if ((errno == ENOSPC) + && !ctx->al_entry + && !ntfs_inode_add_attrlist(icx->ni)) { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, + &ctx); + if (ir + && !ntfs_attr_record_move_away(ctx, ix_root_size + - le32_to_cpu(ctx->attr->value_length))) { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + goto retry; + } + } + /* FIXME: revert index root */ + goto clear_bmp; + } + /* + * FIXME: do it earlier if we have enough space in IR (should always), + * so in error case we wouldn't lose the IB. + */ + ntfs_ie_set_vcn(ie, new_ib_vcn); + + ret = STATUS_OK; +err_out: + free(ib); + ntfs_attr_put_search_ctx(ctx); +out: + return ret; +clear_bmp: + ntfs_ibm_clear(icx, new_ib_vcn); + goto err_out; +} + +/** + * ntfs_ir_truncate - Truncate index root attribute + * + * Returns STATUS_OK, STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT or STATUS_ERROR. + */ +static int ntfs_ir_truncate(ntfs_index_context *icx, int data_size) +{ + ntfs_attr *na; + int ret; + + ntfs_log_trace("Entering\n"); + + na = ntfs_attr_open(icx->ni, AT_INDEX_ROOT, icx->name, icx->name_len); + if (!na) { + ntfs_log_perror("Failed to open INDEX_ROOT"); + return STATUS_ERROR; + } + /* + * INDEX_ROOT must be resident and its entries can be moved to + * INDEX_BLOCK, so ENOSPC isn't a real error. + */ + ret = ntfs_attr_truncate(na, data_size + offsetof(INDEX_ROOT, index)); + if (ret == STATUS_OK) { + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; + + icx->ir->index.allocated_size = cpu_to_le32(data_size); + + } else if (ret == STATUS_ERROR) + ntfs_log_perror("Failed to truncate INDEX_ROOT"); + + ntfs_attr_close(na); + return ret; +} + +/** + * ntfs_ir_make_space - Make more space for the index root attribute + * + * On success return STATUS_OK or STATUS_KEEP_SEARCHING. + * On error return STATUS_ERROR. + */ +static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size) +{ + int ret; + + ntfs_log_trace("Entering\n"); + + ret = ntfs_ir_truncate(icx, data_size); + if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { + + ret = ntfs_ir_reparent(icx); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; + else + ntfs_log_perror("Failed to nodify INDEX_ROOT"); + } + + return ret; +} + +/* + * NOTE: 'ie' must be a copy of a real index entry. + */ +static int ntfs_ie_add_vcn(INDEX_ENTRY **ie) +{ + INDEX_ENTRY *p, *old = *ie; + + old->length = cpu_to_le16(le16_to_cpu(old->length) + sizeof(VCN)); + p = realloc(old, le16_to_cpu(old->length)); + if (!p) + return STATUS_ERROR; + + p->ie_flags |= INDEX_ENTRY_NODE; + *ie = p; + + return STATUS_OK; +} + +static int ntfs_ih_insert(INDEX_HEADER *ih, INDEX_ENTRY *orig_ie, VCN new_vcn, + int pos) +{ + INDEX_ENTRY *ie_node, *ie; + int ret = STATUS_ERROR; + VCN old_vcn; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_dup(orig_ie); + if (!ie) + return STATUS_ERROR; + + if (!(ie->ie_flags & INDEX_ENTRY_NODE)) + if (ntfs_ie_add_vcn(&ie)) + goto out; + + ie_node = ntfs_ie_get_by_pos(ih, pos); + old_vcn = ntfs_ie_get_vcn(ie_node); + ntfs_ie_set_vcn(ie_node, new_vcn); + + ntfs_ie_insert(ih, ie, ie_node); + ntfs_ie_set_vcn(ie_node, old_vcn); + ret = STATUS_OK; +out: + free(ie); + + return ret; +} + +static VCN ntfs_icx_parent_vcn(ntfs_index_context *icx) +{ + return icx->parent_vcn[icx->pindex]; +} + +static VCN ntfs_icx_parent_pos(ntfs_index_context *icx) +{ + return icx->parent_pos[icx->pindex]; +} + + +static int ntfs_ir_insert_median(ntfs_index_context *icx, INDEX_ENTRY *median, + VCN new_vcn) +{ + u32 new_size; + int ret; + + ntfs_log_trace("Entering\n"); + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; + + new_size = le32_to_cpu(icx->ir->index.index_length) + + le16_to_cpu(median->length); + if (!(median->ie_flags & INDEX_ENTRY_NODE)) + new_size += sizeof(VCN); + + ret = ntfs_ir_make_space(icx, new_size); + if (ret != STATUS_OK) + return ret; + + icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); + if (!icx->ir) + return STATUS_ERROR; + + return ntfs_ih_insert(&icx->ir->index, median, new_vcn, + ntfs_icx_parent_pos(icx)); +} + +static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib); + +/** + * On success return STATUS_OK or STATUS_KEEP_SEARCHING. + * On error return STATUS_ERROR. + */ +static int ntfs_ib_insert(ntfs_index_context *icx, INDEX_ENTRY *ie, VCN new_vcn) +{ + INDEX_BLOCK *ib; + u32 idx_size, allocated_size; + int err = STATUS_ERROR; + VCN old_vcn; + + ntfs_log_trace("Entering\n"); + + ib = ntfs_malloc(icx->block_size); + if (!ib) + return -1; + + old_vcn = ntfs_icx_parent_vcn(icx); + + if (ntfs_ib_read(icx, old_vcn, ib)) + goto err_out; + + idx_size = le32_to_cpu(ib->index.index_length); + allocated_size = le32_to_cpu(ib->index.allocated_size); + /* FIXME: sizeof(VCN) should be included only if ie has no VCN */ + if (idx_size + le16_to_cpu(ie->length) + sizeof(VCN) > allocated_size) { + err = ntfs_ib_split(icx, ib); + if (err == STATUS_OK) + err = STATUS_KEEP_SEARCHING; + goto err_out; + } + + if (ntfs_ih_insert(&ib->index, ie, new_vcn, ntfs_icx_parent_pos(icx))) + goto err_out; + + if (ntfs_ib_write(icx, ib)) + goto err_out; + + err = STATUS_OK; +err_out: + free(ib); + return err; +} + +/** + * ntfs_ib_split - Split an index block + * + * On success return STATUS_OK or STATUS_KEEP_SEARCHING. + * On error return is STATUS_ERROR. + */ +static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib) +{ + INDEX_ENTRY *median; + VCN new_vcn; + int ret; + + ntfs_log_trace("Entering\n"); + + if (ntfs_icx_parent_dec(icx)) + return STATUS_ERROR; + + median = ntfs_ie_get_median(&ib->index); + new_vcn = ntfs_ibm_get_free(icx); + if (new_vcn == -1) + return STATUS_ERROR; + + if (ntfs_ib_copy_tail(icx, ib, median, new_vcn)) { + ntfs_ibm_clear(icx, new_vcn); + return STATUS_ERROR; + } + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + ret = ntfs_ir_insert_median(icx, median, new_vcn); + else + ret = ntfs_ib_insert(icx, median, new_vcn); + + if (ret != STATUS_OK) { + ntfs_ibm_clear(icx, new_vcn); + return ret; + } + + ret = ntfs_ib_cut_tail(icx, ib, median); + + return ret; +} + +/* JPA static */ +int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie) +{ + INDEX_HEADER *ih; + int allocated_size, new_size; + int ret = STATUS_ERROR; + +#ifdef DEBUG +/* removed by JPA to make function usable for security indexes + char *fn; + fn = ntfs_ie_filename_get(ie); + ntfs_log_trace("file: '%s'\n", fn); + ntfs_attr_name_free(&fn); +*/ +#endif + + while (1) { + + if (!ntfs_index_lookup(&ie->key, le16_to_cpu(ie->key_length), icx)) { + errno = EEXIST; + ntfs_log_perror("Index already have such entry"); + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_perror("Failed to find place for new entry"); + goto err_out; + } + + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + allocated_size = le32_to_cpu(ih->allocated_size); + new_size = le32_to_cpu(ih->index_length) + le16_to_cpu(ie->length); + + if (new_size <= allocated_size) + break; + + ntfs_log_trace("index block sizes: allocated: %d needed: %d\n", + allocated_size, new_size); + + if (icx->is_in_root) { + if (ntfs_ir_make_space(icx, new_size) == STATUS_ERROR) + goto err_out; + } else { + if (ntfs_ib_split(icx, icx->ib) == STATUS_ERROR) + goto err_out; + } + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } + + ntfs_ie_insert(ih, ie, icx->entry); + ntfs_index_entry_mark_dirty(icx); + + ret = STATUS_OK; +err_out: + ntfs_log_trace("%s\n", ret ? "Failed" : "Done"); + return ret; +} + +/** + * ntfs_index_add_filename - add filename to directory index + * @ni: ntfs inode describing directory to which index add filename + * @fn: FILE_NAME attribute to add + * @mref: reference of the inode which @fn describes + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref) +{ + INDEX_ENTRY *ie; + ntfs_index_context *icx; + int fn_size, ie_size, err, ret = -1; + + ntfs_log_trace("Entering\n"); + + if (!ni || !fn) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + fn_size = (fn->file_name_length * sizeof(ntfschar)) + + sizeof(FILE_NAME_ATTR); + ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7; + + ie = ntfs_calloc(ie_size); + if (!ie) + return -1; + + ie->indexed_file = cpu_to_le64(mref); + ie->length = cpu_to_le16(ie_size); + ie->key_length = cpu_to_le16(fn_size); + memcpy(&ie->key, fn, fn_size); + + icx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); + if (!icx) + goto out; + + ret = ntfs_ie_add(icx, ie); + err = errno; + ntfs_index_ctx_put(icx); + errno = err; +out: + free(ie); + return ret; +} + +static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih, + INDEX_ENTRY *ie, INDEX_BLOCK *ib) +{ + INDEX_ENTRY *ie_roam; + int ret = STATUS_ERROR; + + ntfs_log_trace("Entering\n"); + + ie_roam = ntfs_ie_dup_novcn(ie); + if (!ie_roam) + return STATUS_ERROR; + + ntfs_ie_delete(ih, ie); + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + else + if (ntfs_ib_write(icx, ib)) + goto out; + + ntfs_index_ctx_reinit(icx); + + ret = ntfs_ie_add(icx, ie_roam); +out: + free(ie_roam); + return ret; +} + +/** + * Used if an empty index block to be deleted has END entry as the parent + * in the INDEX_ROOT which is the only one there. + */ +static void ntfs_ir_leafify(ntfs_index_context *icx, INDEX_HEADER *ih) +{ + INDEX_ENTRY *ie; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_first(ih); + ie->ie_flags &= ~INDEX_ENTRY_NODE; + ie->length = cpu_to_le16(le16_to_cpu(ie->length) - sizeof(VCN)); + + ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) - sizeof(VCN)); + ih->ih_flags &= ~LARGE_INDEX; + + /* Not fatal error */ + ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); +} + +/** + * Used if an empty index block to be deleted has END entry as the parent + * in the INDEX_ROOT which is not the only one there. + */ +static int ntfs_ih_reparent_end(ntfs_index_context *icx, INDEX_HEADER *ih, + INDEX_BLOCK *ib) +{ + INDEX_ENTRY *ie, *ie_prev; + + ntfs_log_trace("Entering\n"); + + ie = ntfs_ie_get_by_pos(ih, ntfs_icx_parent_pos(icx)); + ie_prev = ntfs_ie_prev(ih, ie); + + ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(ie_prev)); + + return ntfs_ih_takeout(icx, ih, ie_prev, ib); +} + +static int ntfs_index_rm_leaf(ntfs_index_context *icx) +{ + INDEX_BLOCK *ib = NULL; + INDEX_HEADER *parent_ih; + INDEX_ENTRY *ie; + int ret = STATUS_ERROR; + + ntfs_log_trace("pindex: %d\n", icx->pindex); + + if (ntfs_icx_parent_dec(icx)) + return STATUS_ERROR; + + if (ntfs_ibm_clear(icx, icx->parent_vcn[icx->pindex + 1])) + return STATUS_ERROR; + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + parent_ih = &icx->ir->index; + else { + ib = ntfs_malloc(icx->block_size); + if (!ib) + return STATUS_ERROR; + + if (ntfs_ib_read(icx, ntfs_icx_parent_vcn(icx), ib)) + goto out; + + parent_ih = &ib->index; + } + + ie = ntfs_ie_get_by_pos(parent_ih, ntfs_icx_parent_pos(icx)); + if (!ntfs_ie_end(ie)) { + ret = ntfs_ih_takeout(icx, parent_ih, ie, ib); + goto out; + } + + if (ntfs_ih_zero_entry(parent_ih)) { + + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) { + ntfs_ir_leafify(icx, parent_ih); + goto ok; + } + + ret = ntfs_index_rm_leaf(icx); + goto out; + } + + if (ntfs_ih_reparent_end(icx, parent_ih, ib)) + goto out; +ok: + ret = STATUS_OK; +out: + free(ib); + return ret; +} + +static int ntfs_index_rm_node(ntfs_index_context *icx) +{ + int entry_pos, pindex; + VCN vcn; + INDEX_BLOCK *ib = NULL; + INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry; + INDEX_HEADER *ih; + u32 new_size; + int delta, ret = STATUS_ERROR; + + ntfs_log_trace("Entering\n"); + + if (!icx->ia_na) { + icx->ia_na = ntfs_ia_open(icx, icx->ni); + if (!icx->ia_na) + return STATUS_ERROR; + } + + ib = ntfs_malloc(icx->block_size); + if (!ib) + return STATUS_ERROR; + + ie_succ = ntfs_ie_get_next(icx->entry); + entry_pos = icx->parent_pos[icx->pindex]++; + pindex = icx->pindex; +descend: + vcn = ntfs_ie_get_vcn(ie_succ); + if (ntfs_ib_read(icx, vcn, ib)) + goto out; + + ie_succ = ntfs_ie_get_first(&ib->index); + + if (ntfs_icx_parent_inc(icx)) + goto out; + + icx->parent_vcn[icx->pindex] = vcn; + icx->parent_pos[icx->pindex] = 0; + + if ((ib->index.ih_flags & NODE_MASK) == INDEX_NODE) + goto descend; + + if (ntfs_ih_zero_entry(&ib->index)) { + errno = EIO; + ntfs_log_perror("Empty index block"); + goto out; + } + + ie = ntfs_ie_dup(ie_succ); + if (!ie) + goto out; + + if (ntfs_ie_add_vcn(&ie)) + goto out2; + + ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(icx->entry)); + + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + delta = le16_to_cpu(ie->length) - le16_to_cpu(icx->entry->length); + new_size = le32_to_cpu(ih->index_length) + delta; + if (delta > 0) { + if (icx->is_in_root) { + ret = ntfs_ir_make_space(icx, new_size); + if (ret != STATUS_OK) + goto out2; + + ih = &icx->ir->index; + entry = ntfs_ie_get_by_pos(ih, entry_pos); + + } else if (new_size > le32_to_cpu(ih->allocated_size)) { + icx->pindex = pindex; + ret = ntfs_ib_split(icx, icx->ib); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; + goto out2; + } + } + + ntfs_ie_delete(ih, entry); + ntfs_ie_insert(ih, ie, entry); + + if (icx->is_in_root) { + if (ntfs_ir_truncate(icx, new_size)) + goto out2; + } else + if (ntfs_icx_ib_write(icx)) + goto out2; + + ntfs_ie_delete(&ib->index, ie_succ); + + if (ntfs_ih_zero_entry(&ib->index)) { + if (ntfs_index_rm_leaf(icx)) + goto out2; + } else + if (ntfs_ib_write(icx, ib)) + goto out2; + + ret = STATUS_OK; +out2: + free(ie); +out: + free(ib); + return ret; +} + +/** + * ntfs_index_rm - remove entry from the index + * @icx: index context describing entry to delete + * + * Delete entry described by @icx from the index. Index context is always + * reinitialized after use of this function, so it can be used for index + * lookup once again. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +/*static JPA*/ +int ntfs_index_rm(ntfs_index_context *icx) +{ + INDEX_HEADER *ih; + int err, ret = STATUS_OK; + + ntfs_log_trace("Entering\n"); + + if (!icx || (!icx->ib && !icx->ir) || ntfs_ie_end(icx->entry)) { + ntfs_log_error("Invalid arguments.\n"); + errno = EINVAL; + goto err_out; + } + if (icx->is_in_root) + ih = &icx->ir->index; + else + ih = &icx->ib->index; + + if (icx->entry->ie_flags & INDEX_ENTRY_NODE) { + + ret = ntfs_index_rm_node(icx); + + } else if (icx->is_in_root || !ntfs_ih_one_entry(ih)) { + + ntfs_ie_delete(ih, icx->entry); + + if (icx->is_in_root) { + err = ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); + if (err != STATUS_OK) + goto err_out; + } else + if (ntfs_icx_ib_write(icx)) + goto err_out; + } else { + if (ntfs_index_rm_leaf(icx)) + goto err_out; + } +out: + return ret; +err_out: + ret = STATUS_ERROR; + goto out; +} + +int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni, + const void *key, const int keylen) +{ + int ret = STATUS_ERROR; + ntfs_index_context *icx; + + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (!icx) + return -1; + + while (1) { + + if (ntfs_index_lookup(key, keylen, icx)) + goto err_out; + + if ((((FILE_NAME_ATTR *)icx->data)->file_attributes & + FILE_ATTR_REPARSE_POINT) + && !ntfs_possible_symlink(ni)) { + errno = EOPNOTSUPP; + goto err_out; + } + + ret = ntfs_index_rm(icx); + if (ret == STATUS_ERROR) + goto err_out; + else if (ret == STATUS_OK) + break; + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); +out: + ntfs_index_ctx_put(icx); + return ret; +err_out: + ret = STATUS_ERROR; + ntfs_log_perror("Delete failed"); + goto out; +} + +/** + * ntfs_index_root_get - read the index root of an attribute + * @ni: open ntfs inode in which the ntfs attribute resides + * @attr: attribute for which we want its index root + * + * This function will read the related index root an ntfs attribute. + * + * On success a buffer is allocated with the content of the index root + * and which needs to be freed when it's not needed anymore. + * + * On error NULL is returned with errno set to the error code. + */ +INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr) +{ + ntfs_attr_search_ctx *ctx; + ntfschar *name; + INDEX_ROOT *root = NULL; + + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); + + if (!ntfs_ir_lookup(ni, name, attr->name_length, &ctx)) + return NULL; + + root = ntfs_malloc(sizeof(INDEX_ROOT)); + if (!root) + goto out; + + *root = *((INDEX_ROOT *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset))); +out: + ntfs_attr_put_search_ctx(ctx); + return root; +} + + +/* + * Walk down the index tree (leaf bound) + * until there are no subnode in the first index entry + * returns the entry at the bottom left in subnode + */ + +static INDEX_ENTRY *ntfs_index_walk_down(INDEX_ENTRY *ie, + ntfs_index_context *ictx) +{ + INDEX_ENTRY *entry; + s64 vcn; + + entry = ie; + do { + vcn = ntfs_ie_get_vcn(entry); + if (ictx->is_in_root) { + + /* down from level zero */ + + ictx->ir = (INDEX_ROOT*)NULL; + ictx->ib = (INDEX_BLOCK*)ntfs_malloc(ictx->block_size); + ictx->pindex = 1; + ictx->is_in_root = FALSE; + } else { + + /* down from non-zero level */ + + ictx->pindex++; + } + ictx->parent_pos[ictx->pindex] = 0; + ictx->parent_vcn[ictx->pindex] = vcn; + if (!ntfs_ib_read(ictx,vcn,ictx->ib)) { + ictx->entry = ntfs_ie_get_first(&ictx->ib->index); + entry = ictx->entry; + } else + entry = (INDEX_ENTRY*)NULL; + } while (entry && (entry->ie_flags & INDEX_ENTRY_NODE)); + return (entry); +} + +/* + * Walk up the index tree (root bound) + * until there is a valid data entry in parent + * returns the parent entry or NULL if no more parent + */ + +static INDEX_ENTRY *ntfs_index_walk_up(INDEX_ENTRY *ie, + ntfs_index_context *ictx) +{ + INDEX_ENTRY *entry; + s64 vcn; + + entry = ie; + if (ictx->pindex > 0) { + do { + ictx->pindex--; + if (!ictx->pindex) { + + /* we have reached the root */ + + free(ictx->ib); + ictx->ib = (INDEX_BLOCK*)NULL; + ictx->is_in_root = TRUE; + /* a new search context is to be allocated */ + if (ictx->actx) + free(ictx->actx); + ictx->ir = ntfs_ir_lookup(ictx->ni, + ictx->name, ictx->name_len, + &ictx->actx); + if (ictx->ir) + entry = ntfs_ie_get_by_pos( + &ictx->ir->index, + ictx->parent_pos[ictx->pindex]); + else + entry = (INDEX_ENTRY*)NULL; + } else { + /* up into non-root node */ + vcn = ictx->parent_vcn[ictx->pindex]; + if (!ntfs_ib_read(ictx,vcn,ictx->ib)) { + entry = ntfs_ie_get_by_pos( + &ictx->ib->index, + ictx->parent_pos[ictx->pindex]); + } else + entry = (INDEX_ENTRY*)NULL; + } + ictx->entry = entry; + } while (entry && (ictx->pindex > 0) + && (entry->ie_flags & INDEX_ENTRY_END)); + } else + entry = (INDEX_ENTRY*)NULL; + return (entry); +} + +/* + * Get next entry in an index according to collating sequence. + * Must be initialized through a ntfs_index_lookup() + * + * Returns next entry or NULL if none + * + * Sample layout : + * + * +---+---+---+---+---+---+---+---+ n ptrs to subnodes + * | | | 10| 25| 33| | | | n-1 keys in between + * +---+---+---+---+---+---+---+---+ no key in last entry + * | A | A + * | | | +-------------------------------+ + * +--------------------------+ | +-----+ | + * | +--+ | | + * V | V | + * +---+---+---+---+---+---+---+---+ | +---+---+---+---+---+---+---+---+ + * | 11| 12| 13| 14| 15| 16| 17| | | | 26| 27| 28| 29| 30| 31| 32| | + * +---+---+---+---+---+---+---+---+ | +---+---+---+---+---+---+---+---+ + * | | + * +-----------------------+ | + * | | + * +---+---+---+---+---+---+---+---+ + * | 18| 19| 20| 21| 22| 23| 24| | + * +---+---+---+---+---+---+---+---+ + */ + +INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie, ntfs_index_context *ictx) +{ + INDEX_ENTRY *next; + int flags; + + /* + * lookup() may have returned an invalid node + * when searching for a partial key + * if this happens, walk up + */ + + if (ie->ie_flags & INDEX_ENTRY_END) + next = ntfs_index_walk_up(ie, ictx); + else { + /* + * get next entry in same node + * there is always one after any entry with data + */ + + next = (INDEX_ENTRY*)((char*)ie + le16_to_cpu(ie->length)); + ++ictx->parent_pos[ictx->pindex]; + flags = next->ie_flags; + + /* walk down if it has a subnode */ + + if (flags & INDEX_ENTRY_NODE) { + next = ntfs_index_walk_down(next,ictx); + } else { + + /* walk up it has no subnode, nor data */ + + if (flags & INDEX_ENTRY_END) { + next = ntfs_index_walk_up(next, ictx); + } + } + } + /* return NULL if stuck at end of a block */ + + if (next && (next->ie_flags & INDEX_ENTRY_END)) + next = (INDEX_ENTRY*)NULL; + return (next); +} + + diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c new file mode 100755 index 0000000000000000000000000000000000000000..a4a01348be2620072ee6a6f5e9b78cc43ac177a6 --- /dev/null +++ b/libntfs-3g/inode.c @@ -0,0 +1,1608 @@ +/** + * inode.c - Inode handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2009-2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#endif + +#include "param.h" +#include "compat.h" +#include "types.h" +#include "volume.h" +#include "cache.h" +#include "inode.h" +#include "attrib.h" +#include "debug.h" +#include "mft.h" +#include "attrlist.h" +#include "runlist.h" +#include "lcnalloc.h" +#include "index.h" +#include "dir.h" +#include "ntfstime.h" +#include "logging.h" +#include "misc.h" + +ntfs_inode *ntfs_inode_base(ntfs_inode *ni) +{ + if (ni->nr_extents == -1) + return ni->base_ni; + return ni; +} + +/** + * ntfs_inode_mark_dirty - set the inode (and its base inode if it exists) dirty + * @ni: ntfs inode to set dirty + * + * Set the inode @ni dirty so it is written out later (at the latest at + * ntfs_inode_close() time). If @ni is an extent inode, set the base inode + * dirty, too. + * + * This function cannot fail. + */ +void ntfs_inode_mark_dirty(ntfs_inode *ni) +{ + NInoSetDirty(ni); + if (ni->nr_extents == -1) + NInoSetDirty(ni->base_ni); +} + +/** + * __ntfs_inode_allocate - Create and initialise an NTFS inode object + * @vol: + * + * Description... + * + * Returns: + */ +static ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol) +{ + ntfs_inode *ni; + + ni = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode)); + if (ni) + ni->vol = vol; + return ni; +} + +/** + * ntfs_inode_allocate - Create an NTFS inode object + * @vol: + * + * Description... + * + * Returns: + */ +ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol) +{ + return __ntfs_inode_allocate(vol); +} + +/** + * __ntfs_inode_release - Destroy an NTFS inode object + * @ni: + * + * Description... + * + * Returns: + */ +static void __ntfs_inode_release(ntfs_inode *ni) +{ + if (NInoDirty(ni)) + ntfs_log_error("Releasing dirty inode %lld!\n", + (long long)ni->mft_no); + if (NInoAttrList(ni) && ni->attr_list) + free(ni->attr_list); + free(ni->mrec); + free(ni); + return; +} + +/** + * ntfs_inode_open - open an inode ready for access + * @vol: volume to get the inode from + * @mref: inode number / mft record number to open + * + * Allocate an ntfs_inode structure and initialize it for the given inode + * specified by @mref. @mref specifies the inode number / mft record to read, + * including the sequence number, which can be 0 if no sequence number checking + * is to be performed. + * + * Then, allocate a buffer for the mft record, read the mft record from the + * volume @vol, and attach it to the ntfs_inode structure (->mrec). The + * mft record is mst deprotected and sanity checked for validity and we abort + * if deprotection or checks fail. + * + * Finally, search for an attribute list attribute in the mft record and if one + * is found, load the attribute list attribute value and attach it to the + * ntfs_inode structure (->attr_list). Also set the NI_AttrList bit to indicate + * this. + * + * Return a pointer to the ntfs_inode structure on success or NULL on error, + * with errno set to the error code. + */ +static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref) +{ + s64 l; + ntfs_inode *ni = NULL; + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + le32 lthle; + int olderrno; + + ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); + if (!vol) { + errno = EINVAL; + goto out; + } + ni = __ntfs_inode_allocate(vol); + if (!ni) + goto out; + if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL)) + goto err_out; + if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) { + errno = ENOENT; + goto err_out; + } + ni->mft_no = MREF(mref); + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + goto err_out; + /* Receive some basic information about inode. */ + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (!ni->mrec->base_mft_record) + ntfs_log_perror("No STANDARD_INFORMATION in base record" + " %lld", (long long)MREF(mref)); + goto put_err_out; + } + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + ni->flags = std_info->file_attributes; + ni->creation_time = std_info->creation_time; + ni->last_data_change_time = std_info->last_data_change_time; + ni->last_mft_change_time = std_info->last_mft_change_time; + ni->last_access_time = std_info->last_access_time; + /* JPA insert v3 extensions if present */ + /* length may be seen as 72 (v1.x) or 96 (v3.x) */ + lthle = ctx->attr->length; + if (le32_to_cpu(lthle) > sizeof(STANDARD_INFORMATION)) { + set_nino_flag(ni, v3_Extensions); + ni->owner_id = std_info->owner_id; + ni->security_id = std_info->security_id; + ni->quota_charged = std_info->quota_charged; + ni->usn = std_info->usn; + } else { + clear_nino_flag(ni, v3_Extensions); + ni->owner_id = const_cpu_to_le32(0); + ni->security_id = const_cpu_to_le32(0); + } + /* Set attribute list information. */ + olderrno = errno; + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + goto put_err_out; + /* Attribute list attribute does not present. */ + /* restore previous errno to avoid misinterpretation */ + errno = olderrno; + goto get_size; + } + NInoSetAttrList(ni); + l = ntfs_get_attribute_value_length(ctx->attr); + if (!l) + goto put_err_out; + if (l > 0x40000) { + errno = EIO; + ntfs_log_perror("Too large attrlist attribute (%lld), inode " + "%lld", (long long)l, (long long)MREF(mref)); + goto put_err_out; + } + ni->attr_list_size = l; + ni->attr_list = ntfs_malloc(ni->attr_list_size); + if (!ni->attr_list) + goto put_err_out; + l = ntfs_get_attribute_value(vol, ctx->attr, ni->attr_list); + if (!l) + goto put_err_out; + if (l != ni->attr_list_size) { + errno = EIO; + ntfs_log_perror("Unexpected attrlist size (%lld <> %u), inode " + "%lld", (long long)l, ni->attr_list_size, + (long long)MREF(mref)); + goto put_err_out; + } +get_size: + olderrno = errno; + if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { + if (errno != ENOENT) + goto put_err_out; + /* Directory or special file. */ + /* restore previous errno to avoid misinterpretation */ + errno = olderrno; + ni->data_size = ni->allocated_size = 0; + } else { + if (ctx->attr->non_resident) { + ni->data_size = sle64_to_cpu(ctx->attr->data_size); + if (ctx->attr->flags & + (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) + ni->allocated_size = sle64_to_cpu( + ctx->attr->compressed_size); + else + ni->allocated_size = sle64_to_cpu( + ctx->attr->allocated_size); + } else { + ni->data_size = le32_to_cpu(ctx->attr->value_length); + ni->allocated_size = (ni->data_size + 7) & ~7; + } + set_nino_flag(ni,KnownSize); + } + ntfs_attr_put_search_ctx(ctx); +out: + ntfs_log_leave("\n"); + return ni; + +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + __ntfs_inode_release(ni); + ni = NULL; + goto out; +} + +/** + * ntfs_inode_close - close an ntfs inode and free all associated memory + * @ni: ntfs inode to close + * + * Make sure the ntfs inode @ni is clean. + * + * If the ntfs inode @ni is a base inode, close all associated extent inodes, + * then deallocate all memory attached to it, and finally free the ntfs inode + * structure itself. + * + * If it is an extent inode, we disconnect it from its base inode before we + * destroy it. + * + * It is OK to pass NULL to this function, it is just noop in this case. + * + * Return 0 on success or -1 on error with errno set to the error code. On + * error, @ni has not been freed. The user should attempt to handle the error + * and call ntfs_inode_close() again. The following error codes are defined: + * + * EBUSY @ni and/or its attribute list runlist is/are dirty and the + * attempt to write it/them to disk failed. + * EINVAL @ni is invalid (probably it is an extent inode). + * EIO I/O error while trying to write inode to disk. + */ + +int ntfs_inode_real_close(ntfs_inode *ni) +{ + int ret = -1; + + if (!ni) + return 0; + + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); + + /* If we have dirty metadata, write it out. */ + if (NInoDirty(ni) || NInoAttrListDirty(ni)) { + if (ntfs_inode_sync(ni)) { + if (errno != EIO) + errno = EBUSY; + goto err; + } + } + /* Is this a base inode with mapped extent inodes? */ + if (ni->nr_extents > 0) { + while (ni->nr_extents > 0) { + if (ntfs_inode_real_close(ni->extent_nis[0])) { + if (errno != EIO) + errno = EBUSY; + goto err; + } + } + } else if (ni->nr_extents == -1) { + ntfs_inode **tmp_nis; + ntfs_inode *base_ni; + s32 i; + + /* + * If the inode is an extent inode, disconnect it from the + * base inode before destroying it. + */ + base_ni = ni->base_ni; + for (i = 0; i < base_ni->nr_extents; ++i) { + tmp_nis = base_ni->extent_nis; + if (tmp_nis[i] != ni) + continue; + /* Found it. Disconnect. */ + memmove(tmp_nis + i, tmp_nis + i + 1, + (base_ni->nr_extents - i - 1) * + sizeof(ntfs_inode *)); + /* Buffer should be for multiple of four extents. */ + if ((--base_ni->nr_extents) & 3) { + i = -1; + break; + } + /* + * ElectricFence is unhappy with realloc(x,0) as free(x) + * thus we explicitly separate these two cases. + */ + if (base_ni->nr_extents) { + /* Resize the memory buffer. */ + tmp_nis = realloc(tmp_nis, base_ni->nr_extents * + sizeof(ntfs_inode *)); + /* Ignore errors, they don't really matter. */ + if (tmp_nis) + base_ni->extent_nis = tmp_nis; + } else if (tmp_nis) { + free(tmp_nis); + base_ni->extent_nis = (ntfs_inode**)NULL; + } + /* Allow for error checking. */ + i = -1; + break; + } + + /* + * We could successfully sync, so only log this error + * and try to sync other inode extents too. + */ + if (i != -1) + ntfs_log_error("Extent inode %lld was not found\n", + (long long)ni->mft_no); + } + + __ntfs_inode_release(ni); + ret = 0; +err: + ntfs_log_leave("\n"); + return ret; +} + +#if CACHE_NIDATA_SIZE + +/* + * Free an inode structure when there is not more space + * in the cache + */ + +void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached) +{ + ntfs_inode_real_close(((const struct CACHED_NIDATA*)cached)->ni); +} + +/* + * Compute a hash value for an inode entry + */ + +int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item) +{ + return (((const struct CACHED_NIDATA*)item)->inum + % (2*CACHE_NIDATA_SIZE)); +} + +/* + * inum comparing for entering/fetching from cache + */ + +static int idata_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + return (((const struct CACHED_NIDATA*)cached)->inum + != ((const struct CACHED_NIDATA*)wanted)->inum); +} + +/* + * Invalidate an inode entry when not needed anymore. + * The entry should have been synced, it may be reused later, + * if it is requested before it is dropped from cache. + */ + +void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref) +{ + struct CACHED_NIDATA item; + + item.inum = MREF(mref); + item.ni = (ntfs_inode*)NULL; + item.pathname = (const char*)NULL; + item.varsize = 0; + ntfs_invalidate_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare,CACHE_FREE); +} + +#endif + +/* + * Open an inode + * + * When possible, an entry recorded in the cache is reused + * + * **NEVER REOPEN** an inode, this can lead to a duplicated + * cache entry (hard to detect), and to an obsolete one being + * reused. System files are however protected from being cached. + */ + +ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) +{ + ntfs_inode *ni; +#if CACHE_NIDATA_SIZE + struct CACHED_NIDATA item; + struct CACHED_NIDATA *cached; + + /* fetch idata from cache */ + item.inum = MREF(mref); + debug_double_inode(item.inum,1); + item.pathname = (const char*)NULL; + item.varsize = 0; + cached = (struct CACHED_NIDATA*)ntfs_fetch_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare); + if (cached) { + ni = cached->ni; + /* do not keep open entries in cache */ + ntfs_remove_cache(vol->nidata_cache, + (struct CACHED_GENERIC*)cached,0); + } else { + ni = ntfs_inode_real_open(vol, mref); + } + if (!ni) { + debug_double_inode(item.inum, 0); + } +#else + ni = ntfs_inode_real_open(vol, mref); +#endif + return (ni); +} + +/* + * Close an inode entry + * + * If cacheing is in use, the entry is synced and kept available + * in cache for further use. + * + * System files (inode < 16 or having the IS_4 flag) are protected + * against being cached. + */ + +int ntfs_inode_close(ntfs_inode *ni) +{ + int res; +#if CACHE_NIDATA_SIZE + BOOL dirty; + struct CACHED_NIDATA item; + + if (ni) { + debug_double_inode(ni->mft_no,0); + /* do not cache system files : could lead to double entries */ + if (ni->vol && ni->vol->nidata_cache + && ((ni->mft_no == FILE_root) + || ((ni->mft_no >= FILE_first_user) + && !(ni->mrec->flags & MFT_RECORD_IS_4)))) { + /* If we have dirty metadata, write it out. */ + dirty = NInoDirty(ni) || NInoAttrListDirty(ni); + if (dirty) { + res = ntfs_inode_sync(ni); + /* do a real close if sync failed */ + if (res) + ntfs_inode_real_close(ni); + } else + res = 0; + + if (!res) { + /* feed idata into cache */ + item.inum = ni->mft_no; + item.ni = ni; + item.pathname = (const char*)NULL; + item.varsize = 0; + debug_cached_inode(ni); + ntfs_enter_cache(ni->vol->nidata_cache, + GENERIC(&item), idata_cache_compare); + } + } else { + /* cache not ready or system file, really close */ + res = ntfs_inode_real_close(ni); + } + } else + res = 0; +#else + res = ntfs_inode_real_close(ni); +#endif + return (res); +} + +/** + * ntfs_extent_inode_open - load an extent inode and attach it to its base + * @base_ni: base ntfs inode + * @mref: mft reference of the extent inode to load (in little endian) + * + * First check if the extent inode @mref is already attached to the base ntfs + * inode @base_ni, and if so, return a pointer to the attached extent inode. + * + * If the extent inode is not already attached to the base inode, allocate an + * ntfs_inode structure and initialize it for the given inode @mref. @mref + * specifies the inode number / mft record to read, including the sequence + * number, which can be 0 if no sequence number checking is to be performed. + * + * Then, allocate a buffer for the mft record, read the mft record from the + * volume @base_ni->vol, and attach it to the ntfs_inode structure (->mrec). + * The mft record is mst deprotected and sanity checked for validity and we + * abort if deprotection or checks fail. + * + * Finally attach the ntfs inode to its base inode @base_ni and return a + * pointer to the ntfs_inode structure on success or NULL on error, with errno + * set to the error code. + * + * Note, extent inodes are never closed directly. They are automatically + * disposed off by the closing of the base inode. + */ +ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) +{ + u64 mft_no = MREF_LE(mref); + VCN extent_vcn; + runlist_element *rl; + ntfs_volume *vol; + ntfs_inode *ni = NULL; + ntfs_inode **extent_nis; + int i; + + if (!base_ni) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return NULL; + } + + ntfs_log_enter("Opening extent inode %lld (base mft record %lld).\n", + (unsigned long long)mft_no, + (unsigned long long)base_ni->mft_no); + + if (!base_ni->mft_no) { + /* + * When getting extents of MFT, we must be sure + * they are in the MFT part which has already + * been mapped, otherwise we fall into an endless + * recursion. + * Situations have been met where extents locations + * are described in themselves. + * This is a severe error which chkdsk cannot fix. + */ + vol = base_ni->vol; + extent_vcn = mft_no << vol->mft_record_size_bits + >> vol->cluster_size_bits; + rl = vol->mft_na->rl; + if (rl) { + while (rl->length + && ((rl->vcn + rl->length) <= extent_vcn)) + rl++; + } + if (!rl || (rl->lcn < 0)) { + ntfs_log_error("MFT is corrupt, cannot read" + " its unmapped extent record %lld\n", + (long long)mft_no); + ntfs_log_error("Note : chkdsk cannot fix this," + " try ntfsfix\n"); + errno = EIO; + ni = (ntfs_inode*)NULL; + goto out; + } + } + + /* Is the extent inode already open and attached to the base inode? */ + if (base_ni->nr_extents > 0) { + extent_nis = base_ni->extent_nis; + for (i = 0; i < base_ni->nr_extents; i++) { + u16 seq_no; + + ni = extent_nis[i]; + if (mft_no != ni->mft_no) + continue; + /* Verify the sequence number if given. */ + seq_no = MSEQNO_LE(mref); + if (seq_no && seq_no != le16_to_cpu( + ni->mrec->sequence_number)) { + errno = EIO; + ntfs_log_perror("Found stale extent mft " + "reference mft=%lld", + (long long)ni->mft_no); + goto out; + } + goto out; + } + } + /* Wasn't there, we need to load the extent inode. */ + ni = __ntfs_inode_allocate(base_ni->vol); + if (!ni) + goto out; + if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, NULL)) + goto err_out; + ni->mft_no = mft_no; + ni->nr_extents = -1; + ni->base_ni = base_ni; + /* Attach extent inode to base inode, reallocating memory if needed. */ + if (!(base_ni->nr_extents & 3)) { + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + + extent_nis = ntfs_malloc(i); + if (!extent_nis) + goto err_out; + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; +out: + ntfs_log_leave("\n"); + return ni; +err_out: + __ntfs_inode_release(ni); + ni = NULL; + goto out; +} + +/** + * ntfs_inode_attach_all_extents - attach all extents for target inode + * @ni: opened ntfs inode for which perform attach + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_inode_attach_all_extents(ntfs_inode *ni) +{ + ATTR_LIST_ENTRY *ale; + u64 prev_attached = 0; + + if (!ni) { + ntfs_log_trace("Invalid arguments.\n"); + errno = EINVAL; + return -1; + } + + if (ni->nr_extents == -1) + ni = ni->base_ni; + + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + + /* Inode haven't got attribute list, thus nothing to attach. */ + if (!NInoAttrList(ni)) + return 0; + + if (!ni->attr_list) { + ntfs_log_trace("Corrupt in-memory struct.\n"); + errno = EINVAL; + return -1; + } + + /* Walk through attribute list and attach all extents. */ + errno = 0; + ale = (ATTR_LIST_ENTRY *)ni->attr_list; + while ((u8*)ale < ni->attr_list + ni->attr_list_size) { + if (ni->mft_no != MREF_LE(ale->mft_reference) && + prev_attached != MREF_LE(ale->mft_reference)) { + if (!ntfs_extent_inode_open(ni, ale->mft_reference)) { + ntfs_log_trace("Couldn't attach extent inode.\n"); + return -1; + } + prev_attached = MREF_LE(ale->mft_reference); + } + ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); + } + return 0; +} + +/** + * ntfs_inode_sync_standard_information - update standard information attribute + * @ni: ntfs inode to update standard information + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +static int ntfs_inode_sync_standard_information(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + u32 lth; + le32 lthle; + + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to sync standard info (inode %lld)", + (long long)ni->mft_no); + ntfs_attr_put_search_ctx(ctx); + return -1; + } + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + std_info->file_attributes = ni->flags; + if (!test_nino_flag(ni, TimesSet)) { + std_info->creation_time = ni->creation_time; + std_info->last_data_change_time = ni->last_data_change_time; + std_info->last_mft_change_time = ni->last_mft_change_time; + std_info->last_access_time = ni->last_access_time; + } + + /* JPA update v3.x extensions, ensuring consistency */ + + lthle = ctx->attr->length; + lth = le32_to_cpu(lthle); + if (test_nino_flag(ni, v3_Extensions) + && (lth <= sizeof(STANDARD_INFORMATION))) + ntfs_log_error("bad sync of standard information\n"); + + if (lth > sizeof(STANDARD_INFORMATION)) { + std_info->owner_id = ni->owner_id; + std_info->security_id = ni->security_id; + std_info->quota_charged = ni->quota_charged; + std_info->usn = ni->usn; + } + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return 0; +} + +/** + * ntfs_inode_sync_file_name - update FILE_NAME attributes + * @ni: ntfs inode to update FILE_NAME attributes + * + * Update all FILE_NAME attributes for inode @ni in the index. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + ntfs_attr_search_ctx *ctx = NULL; + ntfs_index_context *ictx; + ntfs_inode *index_ni; + FILE_NAME_ATTR *fn; + FILE_NAME_ATTR *fnx; + REPARSE_POINT *rpp; + le32 reparse_tag; + int err = 0; + + ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + /* Collect the reparse tag, if any */ + reparse_tag = cpu_to_le32(0); + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + if (!ntfs_attr_lookup(AT_REPARSE_POINT, NULL, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + rpp = (REPARSE_POINT*)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + reparse_tag = rpp->reparse_tag; + } + ntfs_attr_reinit_search_ctx(ctx); + } + /* Walk through all FILE_NAME attributes and update them. */ + while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { + fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if (MREF_LE(fn->parent_directory) == ni->mft_no) { + /* + * WARNING: We cheat here and obtain 2 attribute + * search contexts for one inode (first we obtained + * above, second will be obtained inside + * ntfs_index_lookup), it's acceptable for library, + * but will deadlock in the kernel. + */ + index_ni = ni; + } else + if (dir_ni) + index_ni = dir_ni; + else + index_ni = ntfs_inode_open(ni->vol, + le64_to_cpu(fn->parent_directory)); + if (!index_ni) { + if (!err) + err = errno; + ntfs_log_perror("Failed to open inode %lld with index", + (long long)le64_to_cpu(fn->parent_directory)); + continue; + } + ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); + if (!ictx) { + if (!err) + err = errno; + ntfs_log_perror("Failed to get index ctx, inode %lld", + (long long)index_ni->mft_no); + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) + err = errno; + continue; + } + if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { + if (!err) { + if (errno == ENOENT) + err = EIO; + else + err = errno; + } + ntfs_log_perror("Index lookup failed, inode %lld", + (long long)index_ni->mft_no); + ntfs_index_ctx_put(ictx); + if (ni != index_ni && ntfs_inode_close(index_ni) && !err) + err = errno; + continue; + } + /* Update flags and file size. */ + fnx = (FILE_NAME_ATTR *)ictx->data; + fnx->file_attributes = + (fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) | + (ni->flags & FILE_ATTR_VALID_FLAGS); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fnx->data_size = fnx->allocated_size + = const_cpu_to_le64(0); + else { + fnx->allocated_size = cpu_to_sle64(ni->allocated_size); + fnx->data_size = cpu_to_sle64(ni->data_size); + /* + * The file name record has also to be fixed if some + * attribute update implied the unnamed data to be + * made non-resident + */ + fn->allocated_size = fnx->allocated_size; + } + /* update or clear the reparse tag in the index */ + fnx->reparse_point_tag = reparse_tag; + if (!test_nino_flag(ni, TimesSet)) { + fnx->creation_time = ni->creation_time; + fnx->last_data_change_time = ni->last_data_change_time; + fnx->last_mft_change_time = ni->last_mft_change_time; + fnx->last_access_time = ni->last_access_time; + } else { + fnx->creation_time = fn->creation_time; + fnx->last_data_change_time = fn->last_data_change_time; + fnx->last_mft_change_time = fn->last_mft_change_time; + fnx->last_access_time = fn->last_access_time; + } + ntfs_index_entry_mark_dirty(ictx); + ntfs_index_ctx_put(ictx); + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) + err = errno; + } + /* Check for real error occurred. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("Attribute lookup failed, inode %lld", + (long long)ni->mft_no); + goto err_out; + } + ntfs_attr_put_search_ctx(ctx); + if (err) { + errno = err; + return -1; + } + return 0; +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_inode_sync - write the inode (and its dirty extents) to disk + * @ni: ntfs inode to write + * + * Write the inode @ni to disk as well as its dirty extent inodes if such + * exist and @ni is a base inode. If @ni is an extent inode, only @ni is + * written completely disregarding its base inode and any other extent inodes. + * + * For a base inode with dirty extent inodes if any writes fail for whatever + * reason, the failing inode is skipped and the sync process is continued. At + * the end the error condition that brought about the failure is returned. Thus + * the smallest amount of data loss possible occurs. + * + * Return 0 on success or -1 on error with errno set to the error code. + * The following error codes are defined: + * EINVAL - Invalid arguments were passed to the function. + * EBUSY - Inode and/or one of its extents is busy, try again later. + * EIO - I/O error while writing the inode (or one of its extents). + */ +static int ntfs_inode_sync_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int ret = 0; + int err = 0; + if (!ni) { + errno = EINVAL; + ntfs_log_error("Failed to sync NULL inode\n"); + return -1; + } + + ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); + + /* Update STANDARD_INFORMATION. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + ntfs_inode_sync_standard_information(ni)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + } + + /* Update FILE_NAME's in the index. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + NInoFileNameTestAndClearDirty(ni) && + ntfs_inode_sync_file_name(ni, dir_ni)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + ntfs_log_perror("Failed to sync FILE_NAME (inode %lld)", + (long long)ni->mft_no); + NInoFileNameSetDirty(ni); + } + + /* Write out attribute list from cache to disk. */ + if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && + NInoAttrList(ni) && NInoAttrListTestAndClearDirty(ni)) { + ntfs_attr *na; + + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + ntfs_log_perror("Attribute list sync failed " + "(open, inode %lld)", + (long long)ni->mft_no); + } + NInoAttrListSetDirty(ni); + goto sync_inode; + } + + if (na->data_size == ni->attr_list_size) { + if (ntfs_attr_pwrite(na, 0, ni->attr_list_size, + ni->attr_list) != ni->attr_list_size) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + ntfs_log_perror("Attribute list sync " + "failed (write, inode %lld)", + (long long)ni->mft_no); + } + NInoAttrListSetDirty(ni); + } + } else { + err = EIO; + ntfs_log_error("Attribute list sync failed (bad size, " + "inode %lld)\n", (long long)ni->mft_no); + NInoAttrListSetDirty(ni); + } + ntfs_attr_close(na); + } + +sync_inode: + /* Write this inode out to the $MFT (and $MFTMirr if applicable). */ + if (NInoTestAndClearDirty(ni)) { + if (ntfs_mft_record_write(ni->vol, ni->mft_no, ni->mrec)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + NInoSetDirty(ni); + ntfs_log_perror("MFT record sync failed, inode %lld", + (long long)ni->mft_no); + } + } + + /* If this is a base inode with extents write all dirty extents, too. */ + if (ni->nr_extents > 0) { + s32 i; + + for (i = 0; i < ni->nr_extents; ++i) { + ntfs_inode *eni; + + eni = ni->extent_nis[i]; + if (!NInoTestAndClearDirty(eni)) + continue; + + if (ntfs_mft_record_write(eni->vol, eni->mft_no, + eni->mrec)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + NInoSetDirty(eni); + ntfs_log_perror("Extent MFT record sync failed," + " inode %lld/%lld", + (long long)ni->mft_no, + (long long)eni->mft_no); + } + } + } + + if (err) { + errno = err; + ret = -1; + } + + ntfs_log_leave("\n"); + return ret; +} + +int ntfs_inode_sync(ntfs_inode *ni) +{ + return (ntfs_inode_sync_in_dir(ni, (ntfs_inode*)NULL)); +} + +/* + * Close an inode with an open parent inode + */ + +int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + + res = ntfs_inode_sync_in_dir(ni, dir_ni); + if (res) { + if (errno != EIO) + errno = EBUSY; + } else + res = ntfs_inode_close(ni); + return (res); +} + +/** + * ntfs_inode_add_attrlist - add attribute list to inode and fill it + * @ni: opened ntfs inode to which add attribute list + * + * Return 0 on success or -1 on error with errno set to the error code. + * The following error codes are defined: + * EINVAL - Invalid arguments were passed to the function. + * EEXIST - Attribute list already exist. + * EIO - Input/Ouput error occurred. + * ENOMEM - Not enough memory to perform add. + */ +int ntfs_inode_add_attrlist(ntfs_inode *ni) +{ + int err; + ntfs_attr_search_ctx *ctx; + u8 *al = NULL, *aln; + int al_len = 0; + ATTR_LIST_ENTRY *ale = NULL; + ntfs_attr *na; + + if (!ni) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + + ntfs_log_trace("inode %llu\n", (unsigned long long) ni->mft_no); + + if (NInoAttrList(ni) || ni->nr_extents) { + errno = EEXIST; + ntfs_log_perror("Inode already has attribute list"); + return -1; + } + + /* Form attribute list. */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + goto err_out; + } + /* Walk through all attributes. */ + while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { + + int ale_size; + + if (ctx->attr->type == AT_ATTRIBUTE_LIST) { + err = EIO; + ntfs_log_perror("Attribute list already present"); + goto put_err_out; + } + + ale_size = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * + ctx->attr->name_length + 7) & ~7; + al_len += ale_size; + + aln = realloc(al, al_len); + if (!aln) { + err = errno; + ntfs_log_perror("Failed to realloc %d bytes", al_len); + goto put_err_out; + } + ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al)); + al = aln; + + memset(ale, 0, ale_size); + + /* Add attribute to attribute list. */ + ale->type = ctx->attr->type; + ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) + + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7); + ale->name_length = ctx->attr->name_length; + ale->name_offset = (u8 *)ale->name - (u8 *)ale; + if (ctx->attr->non_resident) + ale->lowest_vcn = ctx->attr->lowest_vcn; + else + ale->lowest_vcn = 0; + ale->mft_reference = MK_LE_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)); + ale->instance = ctx->attr->instance; + memcpy(ale->name, (u8 *)ctx->attr + + le16_to_cpu(ctx->attr->name_offset), + ctx->attr->name_length * sizeof(ntfschar)); + ale = (ATTR_LIST_ENTRY *)(al + al_len); + } + /* Check for real error occurred. */ + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("%s: Attribute lookup failed, inode %lld", + __FUNCTION__, (long long)ni->mft_no); + goto put_err_out; + } + + /* Set in-memory attribute list. */ + ni->attr_list = al; + ni->attr_list_size = al_len; + NInoSetAttrList(ni); + NInoAttrListSetDirty(ni); + + /* Free space if there is not enough it for $ATTRIBUTE_LIST. */ + if (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use) < + offsetof(ATTR_RECORD, resident_end)) { + if (ntfs_inode_free_space(ni, + offsetof(ATTR_RECORD, resident_end))) { + /* Failed to free space. */ + err = errno; + ntfs_log_perror("Failed to free space for attrlist"); + goto rollback; + } + } + + /* Add $ATTRIBUTE_LIST to mft record. */ + if (ntfs_resident_attr_record_add(ni, + AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) { + err = errno; + ntfs_log_perror("Couldn't add $ATTRIBUTE_LIST to MFT"); + goto rollback; + } + + /* Resize it. */ + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); + if (!na) { + err = errno; + ntfs_log_perror("Failed to open just added $ATTRIBUTE_LIST"); + goto remove_attrlist_record; + } + if (ntfs_attr_truncate(na, al_len)) { + err = errno; + ntfs_log_perror("Failed to resize just added $ATTRIBUTE_LIST"); + ntfs_attr_close(na); + goto remove_attrlist_record;; + } + + ntfs_attr_put_search_ctx(ctx); + ntfs_attr_close(na); + return 0; + +remove_attrlist_record: + /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */ + ni->attr_list = NULL; + NInoClearAttrList(ni); + /* Remove $ATTRIBUTE_LIST record. */ + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (ntfs_attr_record_rm(ctx)) + ntfs_log_perror("Rollback failed to remove attrlist"); + } else + ntfs_log_perror("Rollback failed to find attrlist"); + /* Setup back in-memory runlist. */ + ni->attr_list = al; + ni->attr_list_size = al_len; + NInoSetAttrList(ni); +rollback: + /* + * Scan attribute list for attributes that placed not in the base MFT + * record and move them to it. + */ + ntfs_attr_reinit_search_ctx(ctx); + ale = (ATTR_LIST_ENTRY*)al; + while ((u8*)ale < al + al_len) { + if (MREF_LE(ale->mft_reference) != ni->mft_no) { + if (!ntfs_attr_lookup(ale->type, ale->name, + ale->name_length, + CASE_SENSITIVE, + sle64_to_cpu(ale->lowest_vcn), + NULL, 0, ctx)) { + if (ntfs_attr_record_move_to(ctx, ni)) + ntfs_log_perror("Rollback failed to " + "move attribute"); + } else + ntfs_log_perror("Rollback failed to find attr"); + ntfs_attr_reinit_search_ctx(ctx); + } + ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length)); + } + /* Remove in-memory attribute list. */ + ni->attr_list = NULL; + ni->attr_list_size = 0; + NInoClearAttrList(ni); + NInoAttrListClearDirty(ni); +put_err_out: + ntfs_attr_put_search_ctx(ctx); +err_out: + free(al); + errno = err; + return -1; +} + +/** + * ntfs_inode_free_space - free space in the MFT record of an inode + * @ni: ntfs inode in which MFT record needs more free space + * @size: amount of space needed to free + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_inode_free_space(ntfs_inode *ni, int size) +{ + ntfs_attr_search_ctx *ctx; + int freed; + + if (!ni || size < 0) { + errno = EINVAL; + ntfs_log_perror("%s: ni=%p size=%d", __FUNCTION__, ni, size); + return -1; + } + + ntfs_log_trace("Entering for inode %lld, size %d\n", + (unsigned long long)ni->mft_no, size); + + freed = (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use)); + + if (size <= freed) + return 0; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + /* + * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT + * record, so position search context on the first attribute after them. + */ + if (ntfs_attr_position(AT_FILE_NAME, ctx)) + goto put_err_out; + + while (1) { + int record_size; + /* + * Check whether attribute is from different MFT record. If so, + * find next, because we don't need such. + */ + while (ctx->ntfs_ino->mft_no != ni->mft_no) { +retry: + if (ntfs_attr_position(AT_UNUSED, ctx)) + goto put_err_out; + } + + if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && + ctx->attr->type == AT_DATA) + goto retry; + + if (ctx->attr->type == AT_INDEX_ROOT) + goto retry; + + record_size = le32_to_cpu(ctx->attr->length); + + if (ntfs_attr_record_move_away(ctx, 0)) { + ntfs_log_perror("Failed to move out attribute #2"); + break; + } + freed += record_size; + + /* Check whether we are done. */ + if (size <= freed) { + ntfs_attr_put_search_ctx(ctx); + return 0; + } + /* + * Reposition to first attribute after $STANDARD_INFORMATION + * and $ATTRIBUTE_LIST instead of simply skipping this attribute + * because in the case when we have got only in-memory attribute + * list then ntfs_attr_lookup will fail when it tries to find + * $ATTRIBUTE_LIST. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_position(AT_FILE_NAME, ctx)) + break; + } +put_err_out: + ntfs_attr_put_search_ctx(ctx); + if (errno == ENOSPC) + ntfs_log_trace("No attributes left that could be moved out.\n"); + return -1; +} + +/** + * ntfs_inode_update_times - update selected time fields for ntfs inode + * @ni: ntfs inode for which update time fields + * @mask: select which time fields should be updated + * + * This function updates time fields to current time. Fields to update are + * selected using @mask (see enum @ntfs_time_update_flags for posssible values). + */ +void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) +{ + ntfs_time now; + + if (!ni) { + ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__); + return; + } + + if ((ni->mft_no < FILE_first_user && ni->mft_no != FILE_root) || + NVolReadOnly(ni->vol) || !mask) + return; + + now = ntfs_current_time(); + if (mask & NTFS_UPDATE_ATIME) + ni->last_access_time = now; + if (mask & NTFS_UPDATE_MTIME) + ni->last_data_change_time = now; + if (mask & NTFS_UPDATE_CTIME) + ni->last_mft_change_time = now; + + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); +} + +/** + * ntfs_inode_badclus_bad - check for $Badclus:$Bad data attribute + * @mft_no: mft record number where @attr is present + * @attr: attribute record used to check for the $Bad attribute + * + * Check if the mft record given by @mft_no and @attr contains the bad sector + * list. Please note that mft record numbers describing $Badclus extent inodes + * will not match the current $Badclus:$Bad check. + * + * On success return 1 if the file is $Badclus:$Bad, otherwise return 0. + * On error return -1 with errno set to the error code. + */ +int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) +{ + int len, ret = 0; + ntfschar *ustr; + + if (!attr) { + ntfs_log_error("Invalid argument.\n"); + errno = EINVAL; + return -1; + } + + if (mft_no != FILE_BadClus) + return 0; + + if (attr->type != AT_DATA) + return 0; + + if ((ustr = ntfs_str2ucs("$Bad", &len)) == NULL) { + ntfs_log_perror("Couldn't convert '$Bad' to Unicode"); + return -1; + } + + if (ustr && ntfs_names_are_equal(ustr, len, + (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)), + attr->name_length, 0, NULL, 0)) + ret = 1; + + ntfs_ucsfree(ustr); + + return ret; +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get high precision NTFS times + * + * They are returned in following order : create, update, access, change + * provided they fit in requested size. + * + * Returns the modified size if successfull (or 32 if buffer size is null) + * -errno if failed + */ + +int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + u64 *times; + int ret; + + ret = 0; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to get standard info (inode %lld)", + (long long)ni->mft_no); + } else { + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if (value && (size >= 8)) { + times = (u64*)value; + times[0] = le64_to_cpu(std_info->creation_time); + ret = 8; + if (size >= 16) { + times[1] = le64_to_cpu(std_info->last_data_change_time); + ret = 16; + } + if (size >= 24) { + times[2] = le64_to_cpu(std_info->last_access_time); + ret = 24; + } + if (size >= 32) { + times[3] = le64_to_cpu(std_info->last_mft_change_time); + ret = 32; + } + } else + if (!size) + ret = 32; + else + ret = -ERANGE; + } + ntfs_attr_put_search_ctx(ctx); + } + return (ret ? ret : -errno); +} + +/* + * Set high precision NTFS times + * + * They are expected in this order : create, update, access + * provided they are present in input. The change time is set to + * current time. + * + * The times are inserted directly in the standard_information and + * file names attributes to avoid manipulating low precision times + * + * Returns 0 if success + * -1 if there were an error (described by errno) + */ + +int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, + int flags) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + FILE_NAME_ATTR *fn; + const u64 *times; + ntfs_time now; + int cnt; + int ret; + + ret = -1; + if ((size >= 8) && !(flags & XATTR_CREATE)) { + times = (const u64*)value; + now = ntfs_current_time(); + /* update the standard information attribute */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, + AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to get standard info (inode %lld)", + (long long)ni->mft_no); + } else { + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + /* + * Mark times set to avoid overwriting + * them when the inode is closed. + * The inode structure must also be updated + * (with loss of precision) because of cacheing. + * TODO : use NTFS precision in inode, and + * return sub-second times in getattr() + */ + set_nino_flag(ni, TimesSet); + std_info->creation_time = cpu_to_le64(times[0]); + ni->creation_time + = std_info->creation_time; + if (size >= 16) { + std_info->last_data_change_time = cpu_to_le64(times[1]); + ni->last_data_change_time + = std_info->last_data_change_time; + } + if (size >= 24) { + std_info->last_access_time = cpu_to_le64(times[2]); + ni->last_access_time + = std_info->last_access_time; + } + std_info->last_mft_change_time = now; + ni->last_mft_change_time = now; + ntfs_inode_mark_dirty(ctx->ntfs_ino); + NInoFileNameSetDirty(ni); + + /* update the file names attributes */ + ntfs_attr_reinit_search_ctx(ctx); + cnt = 0; + while (!ntfs_attr_lookup(AT_FILE_NAME, + AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + fn = (FILE_NAME_ATTR*)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + fn->creation_time + = cpu_to_le64(times[0]); + if (size >= 16) + fn->last_data_change_time + = cpu_to_le64(times[1]); + if (size >= 24) + fn->last_access_time + = cpu_to_le64(times[2]); + fn->last_mft_change_time = now; + cnt++; + } + if (cnt) + ret = 0; + else { + ntfs_log_perror("Failed to get file names (inode %lld)", + (long long)ni->mft_no); + } + } + ntfs_attr_put_search_ctx(ctx); + } + } else + if (size < 8) + errno = ERANGE; + else + errno = EEXIST; + return (ret); +} + +#endif /* HAVE_SETXATTR */ diff --git a/libntfs-3g/ioctl.c b/libntfs-3g/ioctl.c new file mode 100755 index 0000000000000000000000000000000000000000..eb7c8e7b6dea4809bddac2642d3489c85d148db7 --- /dev/null +++ b/libntfs-3g/ioctl.c @@ -0,0 +1,378 @@ +/** + * ioctl.c - Processing of ioctls + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2014 Jean-Pierre Andre + * Copyright (c) 2014 Red Hat, Inc. + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#include <syslog.h> + +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifdef HAVE_LINUX_FS_H +#include <linux/fs.h> +#endif + +#include "compat.h" +#include "debug.h" +#include "bitmap.h" +#include "attrib.h" +#include "inode.h" +#include "layout.h" +#include "volume.h" +#include "index.h" +#include "logging.h" +#include "ntfstime.h" +#include "unistr.h" +#include "dir.h" +#include "security.h" +#include "ioctl.h" +#include "misc.h" + +#if defined(FITRIM) && defined(BLKDISCARD) + +/* Issue a TRIM request to the underlying device for the given clusters. */ +static int fstrim_clusters(ntfs_volume *vol, LCN lcn, s64 length) +{ + struct ntfs_device *dev = vol->dev; + uint64_t range[2]; + + ntfs_log_debug("fstrim_clusters: %lld length %lld\n", + (long long) lcn, (long long) length); + + range[0] = lcn << vol->cluster_size_bits; + range[1] = length << vol->cluster_size_bits; + + if (dev->d_ops->ioctl(dev, BLKDISCARD, range) == -1) { + ntfs_log_debug("fstrim_one_cluster: ioctl failed: %m\n"); + return -errno; + } + return 0; +} + +static int read_line(const char *path, char *line, size_t max_bytes) +{ + FILE *fp; + + fp = fopen(path, "r"); + if (fp == NULL) + return -errno; + if (fgets(line, max_bytes, fp) == NULL) { + int ret = -EIO; /* fgets doesn't set errno */ + fclose(fp); + return ret; + } + fclose (fp); + return 0; +} + +static int read_u64(const char *path, u64 *n) +{ + char line[64]; + int ret; + + ret = read_line(path, line, sizeof line); + if (ret) + return ret; + if (sscanf(line, "%" SCNu64, n) != 1) + return -EINVAL; + return 0; +} + +/* Find discard limits for current backing device. + */ +static int fstrim_limits(ntfs_volume *vol, + u64 *discard_alignment, + u64 *discard_granularity, + u64 *discard_max_bytes) +{ + struct stat statbuf; + char path1[80], path2[80]; + int ret; + + /* Stat the backing device. Caller has ensured it is a block device. */ + if (stat(vol->dev->d_name, &statbuf) == -1) { + ntfs_log_debug("fstrim_limits: could not stat %s\n", + vol->dev->d_name); + return -errno; + } + + /* For whole devices, + * /sys/dev/block/MAJOR:MINOR/discard_alignment + * /sys/dev/block/MAJOR:MINOR/queue/discard_granularity + * /sys/dev/block/MAJOR:MINOR/queue/discard_max_bytes + * will exist. + * For partitions, we also need to check the parent device: + * /sys/dev/block/MAJOR:MINOR/../queue/discard_granularity + * /sys/dev/block/MAJOR:MINOR/../queue/discard_max_bytes + */ + snprintf(path1, sizeof path1, "/sys/dev/block/%d:%d", + major(statbuf.st_rdev), minor(statbuf.st_rdev)); + + snprintf(path2, sizeof path2, "%s/discard_alignment", path1); + ret = read_u64(path2, discard_alignment); + if (ret) { + if (ret != -ENOENT) + return ret; + else + /* We would expect this file to exist on all + * modern kernels. But for the sake of very + * old kernels: + */ + goto not_found; + } + + snprintf(path2, sizeof path2, "%s/queue/discard_granularity", path1); + ret = read_u64(path2, discard_granularity); + if (ret) { + if (ret != -ENOENT) + return ret; + else { + snprintf(path2, sizeof path2, + "%s/../queue/discard_granularity", path1); + ret = read_u64(path2, discard_granularity); + if (ret) { + if (ret != -ENOENT) + return ret; + else + goto not_found; + } + } + } + + snprintf(path2, sizeof path2, "%s/queue/discard_max_bytes", path1); + ret = read_u64(path2, discard_max_bytes); + if (ret) { + if (ret != -ENOENT) + return ret; + else { + snprintf(path2, sizeof path2, + "%s/../queue/discard_max_bytes", path1); + ret = read_u64(path2, discard_max_bytes); + if (ret) { + if (ret != -ENOENT) + return ret; + else + goto not_found; + } + } + } + + return 0; + +not_found: + /* If we reach here then we didn't find the device. This is + * not an error, but set discard_max_bytes = 0 to indicate + * that discard is not available. + */ + *discard_alignment = 0; + *discard_granularity = 0; + *discard_max_bytes = 0; + return 0; +} + +#define FSTRIM_BUFSIZ 4096 + +/* Trim the filesystem. + * + * Free blocks between 'start' and 'start+len-1' (both byte offsets) + * are found and TRIM requests are sent to the block device. 'minlen' + * is the minimum continguous free range to discard. + */ +static int fstrim(ntfs_volume *vol, void *data) +{ + struct fstrim_range *range = data; + u64 start = range->start; + u64 len = range->len; + u64 minlen = range->minlen; + u64 discard_alignment, discard_granularity, discard_max_bytes; + u8 *buf = NULL; + LCN start_buf; + int ret; + + ntfs_log_debug("fstrim: start=%llu len=%llu minlen=%llu\n", + (unsigned long long) start, + (unsigned long long) len, + (unsigned long long) minlen); + + /* Fail if user tries to use the fstrim -o/-l/-m options. + * XXX We could fix these limitations in future. + */ + if (start != 0 || len != (uint64_t)-1) { + ntfs_log_debug("fstrim: setting start or length is not supported\n"); + return -EINVAL; + } + if (minlen > vol->cluster_size) { + ntfs_log_debug("fstrim: minlen > cluster size is not supported\n"); + return -EINVAL; + } + + /* Only block devices are supported. It would be possible to + * support backing files (ie. without using loop) but the + * ioctls used to punch holes in files are completely + * different. + */ + if (!NDevBlock(vol->dev)) { + ntfs_log_debug("fstrim: not supported for non-block-device\n"); + return -EOPNOTSUPP; + } + + ret = fstrim_limits(vol, &discard_alignment, + &discard_granularity, &discard_max_bytes); + if (ret) + return ret; + if (discard_alignment != 0) { + ntfs_log_debug("fstrim: backing device is not aligned for discards\n"); + return -EOPNOTSUPP; + } + if (discard_granularity > vol->cluster_size) { + ntfs_log_debug("fstrim: discard granularity of backing device is larger than cluster size\n"); + return -EOPNOTSUPP; + } + if (discard_max_bytes == 0) { + ntfs_log_debug("fstrim: backing device does not support discard (discard_max_bytes == 0)\n"); + return -EOPNOTSUPP; + } + + /* Sync the device before doing anything. */ + ret = ntfs_device_sync(vol->dev); + if (ret) + return ret; + + /* Read through the bitmap. */ + buf = ntfs_malloc(FSTRIM_BUFSIZ); + if (buf == NULL) + return -errno; + for (start_buf = 0; start_buf < vol->nr_clusters; + start_buf += FSTRIM_BUFSIZ * 8) { + s64 count; + s64 br; + LCN end_buf, start_lcn; + + /* start_buf is LCN of first cluster in the current buffer. + * end_buf is LCN of last cluster + 1 in the current buffer. + */ + end_buf = start_buf + FSTRIM_BUFSIZ*8; + if (end_buf > vol->nr_clusters) + end_buf = vol->nr_clusters; + count = (end_buf - start_buf) / 8; + + br = ntfs_attr_pread(vol->lcnbmp_na, start_buf/8, count, buf); + if (br != count) { + if (br >= 0) + ret = -EIO; + else + ret = -errno; + goto free_out; + } + + /* Trim the clusters in large as possible blocks, but + * not larger than discard_max_bytes. + */ + for (start_lcn = start_buf; start_lcn < end_buf; ++start_lcn) { + if (!ntfs_bit_get(buf, start_lcn-start_buf)) { + LCN end_lcn; + + /* Cluster 'start_lcn' is not in use, + * find end of this run. + */ + end_lcn = start_lcn+1; + while (end_lcn < end_buf && + (u64) (end_lcn-start_lcn) << vol->cluster_size_bits + < discard_max_bytes && + !ntfs_bit_get(buf, end_lcn-start_buf)) + end_lcn++; + + ret = fstrim_clusters(vol, + start_lcn, end_lcn-start_lcn); + if (ret) + goto free_out; + + start_lcn = end_lcn-1; + } + } + } + + ret = 0; +free_out: + free(buf); + return ret; +} + +#endif /* FITRIM && BLKDISCARD */ + +int ntfs_ioctl(ntfs_inode *ni, int cmd, void *arg __attribute__((unused)), + unsigned int flags __attribute__((unused)), void *data) +{ + int ret = 0; + + switch (cmd) { +#if defined(FITRIM) && defined(BLKDISCARD) + case FITRIM: + if (!ni || !data) + ret = -EINVAL; + else + ret = fstrim(ni->vol, data); + break; +#else +#warning FITRIM or BLKDISCARD not defined +#endif + default : + ret = -EINVAL; + break; + } + return (ret); +} diff --git a/libntfs-3g/lcnalloc.c b/libntfs-3g/lcnalloc.c new file mode 100755 index 0000000000000000000000000000000000000000..e84d2431b5d730ed3958d6620e35212514dc5d60 --- /dev/null +++ b/libntfs-3g/lcnalloc.c @@ -0,0 +1,771 @@ +/** + * lcnalloc.c - Cluster (de)allocation code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2004 Anton Altaparmakov + * Copyright (c) 2004 Yura Pakhuchiy + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2008-2009 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "types.h" +#include "attrib.h" +#include "bitmap.h" +#include "debug.h" +#include "runlist.h" +#include "volume.h" +#include "lcnalloc.h" +#include "logging.h" +#include "misc.h" + +/* + * Plenty possibilities for big optimizations all over in the cluster + * allocation, however at the moment the dominant bottleneck (~ 90%) is + * the update of the mapping pairs which converges to the cubic Faulhaber's + * formula as the function of the number of extents (fragments, runs). + */ +#define NTFS_LCNALLOC_BSIZE 4096 +#define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE + +enum { + ZONE_MFT = 1, + ZONE_DATA1 = 2, + ZONE_DATA2 = 4 +} ; + +static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc) +{ + ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, (long long)tc); + + if (tc >= end) + *pos = start; + else if (tc >= start) + *pos = tc; +} + +static void ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc) +{ + ntfs_log_trace("tc = %lld, zone = %d\n", (long long)tc, zone); + + if (zone == ZONE_MFT) + ntfs_cluster_set_zone_pos(vol->mft_lcn, vol->mft_zone_end, + &vol->mft_zone_pos, tc); + else if (zone == ZONE_DATA1) + ntfs_cluster_set_zone_pos(vol->mft_zone_end, vol->nr_clusters, + &vol->data1_zone_pos, tc); + else /* zone == ZONE_DATA2 */ + ntfs_cluster_set_zone_pos(0, vol->mft_zone_start, + &vol->data2_zone_pos, tc); +} + +/* + * Unmark full zones when a cluster has been freed in a full zone + * + * Next allocation will reuse the freed cluster + */ + +static void update_full_status(ntfs_volume *vol, LCN lcn) +{ + if (lcn >= vol->mft_zone_end) { + if (vol->full_zones & ZONE_DATA1) { + ntfs_cluster_update_zone_pos(vol, ZONE_DATA1, lcn); + vol->full_zones &= ~ZONE_DATA1; + } + } else + if (lcn < vol->mft_zone_start) { + if (vol->full_zones & ZONE_DATA2) { + ntfs_cluster_update_zone_pos(vol, ZONE_DATA2, lcn); + vol->full_zones &= ~ZONE_DATA2; + } + } else { + if (vol->full_zones & ZONE_MFT) { + ntfs_cluster_update_zone_pos(vol, ZONE_MFT, lcn); + vol->full_zones &= ~ZONE_MFT; + } + } +} + +static s64 max_empty_bit_range(unsigned char *buf, int size) +{ + int i, j, run = 0; + int max_range = 0; + s64 start_pos = -1; + + ntfs_log_trace("Entering\n"); + + i = 0; + while (i < size) { + switch (*buf) { + case 0 : + do { + buf++; + run += 8; + i++; + } while ((i < size) && !*buf); + break; + case 255 : + if (run > max_range) { + max_range = run; + start_pos = (s64)i * 8 - run; + } + run = 0; + do { + buf++; + i++; + } while ((i < size) && (*buf == 255)); + break; + default : + for (j = 0; j < 8; j++) { + + int bit = *buf & (1 << j); + + if (bit) { + if (run > max_range) { + max_range = run; + start_pos = (s64)i * 8 + (j - run); + } + run = 0; + } else + run++; + } + i++; + buf++; + + } + } + + if (run > max_range) + start_pos = (s64)i * 8 - run; + + return start_pos; +} + +static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b, + u8 *writeback) +{ + s64 written; + + ntfs_log_trace("Entering\n"); + + if (!*writeback) + return 0; + + *writeback = 0; + + written = ntfs_attr_pwrite(vol->lcnbmp_na, pos, size, b); + if (written != size) { + if (!written) + errno = EIO; + ntfs_log_perror("Bitmap write error (%lld, %lld)", + (long long)pos, (long long)size); + return -1; + } + + return 0; +} + +/** + * ntfs_cluster_alloc - allocate clusters on an ntfs volume + * @vol: mounted ntfs volume on which to allocate the clusters + * @start_vcn: vcn to use for the first allocated cluster + * @count: number of clusters to allocate + * @start_lcn: starting lcn at which to allocate the clusters (or -1 if none) + * @zone: zone from which to allocate the clusters + * + * Allocate @count clusters preferably starting at cluster @start_lcn or at the + * current allocator position if @start_lcn is -1, on the mounted ntfs volume + * @vol. @zone is either DATA_ZONE for allocation of normal clusters and + * MFT_ZONE for allocation of clusters for the master file table, i.e. the + * $MFT/$DATA attribute. + * + * On success return a runlist describing the allocated cluster(s). + * + * On error return NULL with errno set to the error code. + * + * Notes on the allocation algorithm + * ================================= + * + * There are two data zones. First is the area between the end of the mft zone + * and the end of the volume, and second is the area between the start of the + * volume and the start of the mft zone. On unmodified/standard NTFS 1.x + * volumes, the second data zone doesn't exist due to the mft zone being + * expanded to cover the start of the volume in order to reserve space for the + * mft bitmap attribute. + * + * The complexity stems from the need of implementing the mft vs data zoned + * approach and from the fact that we have access to the lcn bitmap via up to + * NTFS_LCNALLOC_BSIZE bytes at a time, so we need to cope with crossing over + * boundaries of two buffers. Further, the fact that the allocator allows for + * caller supplied hints as to the location of where allocation should begin + * and the fact that the allocator keeps track of where in the data zones the + * next natural allocation should occur, contribute to the complexity of the + * function. But it should all be worthwhile, because this allocator: + * 1) implements MFT zone reservation + * 2) causes reduction in fragmentation. + * The code is not optimized for speed. + */ +runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, + LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone) +{ + LCN zone_start, zone_end; /* current search range */ + LCN last_read_pos, lcn; + LCN bmp_pos; /* current bit position inside the bitmap */ + LCN prev_lcn = 0, prev_run_len = 0; + s64 clusters, br; + runlist *rl = NULL, *trl; + u8 *buf, *byte, bit, writeback; + u8 pass = 1; /* 1: inside zone; 2: start of zone */ + u8 search_zone; /* 4: data2 (start) 1: mft (middle) 2: data1 (end) */ + u8 done_zones = 0; + u8 has_guess, used_zone_pos; + int err = 0, rlpos, rlsize, buf_size; + + ntfs_log_enter("Entering with count = 0x%llx, start_lcn = 0x%llx, " + "zone = %s_ZONE.\n", (long long)count, (long long) + start_lcn, zone == MFT_ZONE ? "MFT" : "DATA"); + + if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na || + (s8)zone < FIRST_ZONE || zone > LAST_ZONE) { + errno = EINVAL; + ntfs_log_perror("%s: vcn: %lld, count: %lld, lcn: %lld", + __FUNCTION__, (long long)start_vcn, + (long long)count, (long long)start_lcn); + goto out; + } + + /* Return empty runlist if @count == 0 */ + if (!count) { + rl = ntfs_malloc(0x1000); + if (rl) { + rl[0].vcn = start_vcn; + rl[0].lcn = LCN_RL_NOT_MAPPED; + rl[0].length = 0; + } + goto out; + } + + buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE); + if (!buf) + goto out; + /* + * If no @start_lcn was requested, use the current zone + * position otherwise use the requested @start_lcn. + */ + has_guess = 1; + zone_start = start_lcn; + + if (zone_start < 0) { + if (zone == DATA_ZONE) + zone_start = vol->data1_zone_pos; + else + zone_start = vol->mft_zone_pos; + has_guess = 0; + } + + used_zone_pos = has_guess ? 0 : 1; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + + if (zone_start < vol->mft_zone_start) { + zone_end = vol->mft_zone_start; + search_zone = ZONE_DATA2; + } else if (zone_start < vol->mft_zone_end) { + zone_end = vol->mft_zone_end; + search_zone = ZONE_MFT; + } else { + zone_end = vol->nr_clusters; + search_zone = ZONE_DATA1; + } + + bmp_pos = zone_start; + + /* Loop until all clusters are allocated. */ + clusters = count; + rlpos = rlsize = 0; + while (1) { + /* check whether we have exhausted the current zone */ + if (search_zone & vol->full_zones) + goto zone_pass_done; + last_read_pos = bmp_pos >> 3; + br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, + NTFS_LCNALLOC_BSIZE, buf); + if (br <= 0) { + if (!br) + goto zone_pass_done; + err = errno; + ntfs_log_perror("Reading $BITMAP failed"); + goto err_ret; + } + /* + * We might have read less than NTFS_LCNALLOC_BSIZE bytes + * if we are close to the end of the attribute. + */ + buf_size = (int)br << 3; + lcn = bmp_pos & 7; + bmp_pos &= ~7; + writeback = 0; + + while (lcn < buf_size) { + byte = buf + (lcn >> 3); + bit = 1 << (lcn & 7); + if (has_guess) { + if (*byte & bit) { + has_guess = 0; + break; + } + } else { + lcn = max_empty_bit_range(buf, br); + if (lcn < 0) + break; + has_guess = 1; + continue; + } + + /* First free bit is at lcn + bmp_pos. */ + + /* Reallocate memory if necessary. */ + if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { + rlsize += 4096; + trl = realloc(rl, rlsize); + if (!trl) { + err = ENOMEM; + ntfs_log_perror("realloc() failed"); + goto wb_err_ret; + } + rl = trl; + } + + /* Allocate the bitmap bit. */ + *byte |= bit; + writeback = 1; + if (vol->free_clusters <= 0) + ntfs_log_error("Non-positive free clusters " + "(%lld)!\n", + (long long)vol->free_clusters); + else + vol->free_clusters--; + + /* + * Coalesce with previous run if adjacent LCNs. + * Otherwise, append a new run. + */ + if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { + ntfs_log_debug("Cluster coalesce: prev_lcn: " + "%lld lcn: %lld bmp_pos: %lld " + "prev_run_len: %lld\n", + (long long)prev_lcn, + (long long)lcn, (long long)bmp_pos, + (long long)prev_run_len); + rl[rlpos - 1].length = ++prev_run_len; + } else { + if (rlpos) + rl[rlpos].vcn = rl[rlpos - 1].vcn + + prev_run_len; + else { + rl[rlpos].vcn = start_vcn; + ntfs_log_debug("Start_vcn: %lld\n", + (long long)start_vcn); + } + + rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; + rl[rlpos].length = prev_run_len = 1; + rlpos++; + } + + ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n", + (long long)rl[rlpos - 1].vcn, + (long long)rl[rlpos - 1].lcn, + (long long)rl[rlpos - 1].length); + /* Done? */ + if (!--clusters) { + if (used_zone_pos) + ntfs_cluster_update_zone_pos(vol, + search_zone, lcn + bmp_pos + 1 + + NTFS_LCNALLOC_SKIP); + goto done_ret; + } + + lcn++; + } + + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { + err = errno; + goto err_ret; + } + + if (!used_zone_pos) { + + used_zone_pos = 1; + + if (search_zone == ZONE_MFT) + zone_start = vol->mft_zone_pos; + else if (search_zone == ZONE_DATA1) + zone_start = vol->data1_zone_pos; + else + zone_start = vol->data2_zone_pos; + + if (!zone_start || zone_start == vol->mft_zone_start || + zone_start == vol->mft_zone_end) + pass = 2; + bmp_pos = zone_start; + } else + bmp_pos += buf_size; + + if (bmp_pos < zone_end) + continue; + +zone_pass_done: + ntfs_log_trace("Finished current zone pass(%i).\n", pass); + if (pass == 1) { + pass = 2; + zone_end = zone_start; + + if (search_zone == ZONE_MFT) + zone_start = vol->mft_zone_start; + else if (search_zone == ZONE_DATA1) + zone_start = vol->mft_zone_end; + else + zone_start = 0; + + /* Sanity check. */ + if (zone_end < zone_start) + zone_end = zone_start; + + bmp_pos = zone_start; + + continue; + } + /* pass == 2 */ +done_zones_check: + done_zones |= search_zone; + vol->full_zones |= search_zone; + if (done_zones < (ZONE_MFT + ZONE_DATA1 + ZONE_DATA2)) { + ntfs_log_trace("Switching zone.\n"); + pass = 1; + if (rlpos) { + LCN tc = rl[rlpos - 1].lcn + + rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP; + + if (used_zone_pos) + ntfs_cluster_update_zone_pos(vol, + search_zone, tc); + } + + switch (search_zone) { + case ZONE_MFT: + ntfs_log_trace("Zone switch: mft -> data1\n"); +switch_to_data1_zone: search_zone = ZONE_DATA1; + zone_start = vol->data1_zone_pos; + zone_end = vol->nr_clusters; + if (zone_start == vol->mft_zone_end) + pass = 2; + break; + case ZONE_DATA1: + ntfs_log_trace("Zone switch: data1 -> data2\n"); + search_zone = ZONE_DATA2; + zone_start = vol->data2_zone_pos; + zone_end = vol->mft_zone_start; + if (!zone_start) + pass = 2; + break; + case ZONE_DATA2: + if (!(done_zones & ZONE_DATA1)) { + ntfs_log_trace("data2 -> data1\n"); + goto switch_to_data1_zone; + } + ntfs_log_trace("Zone switch: data2 -> mft\n"); + search_zone = ZONE_MFT; + zone_start = vol->mft_zone_pos; + zone_end = vol->mft_zone_end; + if (zone_start == vol->mft_zone_start) + pass = 2; + break; + } + + bmp_pos = zone_start; + + if (zone_start == zone_end) { + ntfs_log_trace("Empty zone, skipped.\n"); + goto done_zones_check; + } + + continue; + } + + ntfs_log_trace("All zones are finished, no space on device.\n"); + err = ENOSPC; + goto err_ret; + } +done_ret: + ntfs_log_debug("At done_ret.\n"); + /* Add runlist terminator element. */ + rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; + rl[rlpos].lcn = LCN_RL_NOT_MAPPED; + rl[rlpos].length = 0; + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { + err = errno; + goto err_ret; + } +done_err_ret: + free(buf); + if (err) { + errno = err; + ntfs_log_perror("Failed to allocate clusters"); + rl = NULL; + } +out: + ntfs_log_leave("\n"); + return rl; + +wb_err_ret: + ntfs_log_trace("At wb_err_ret.\n"); + if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) + err = errno; +err_ret: + ntfs_log_trace("At err_ret.\n"); + if (rl) { + /* Add runlist terminator element. */ + rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; + rl[rlpos].lcn = LCN_RL_NOT_MAPPED; + rl[rlpos].length = 0; + ntfs_debug_runlist_dump(rl); + ntfs_cluster_free_from_rl(vol, rl); + free(rl); + rl = NULL; + } + goto done_err_ret; +} + +/** + * ntfs_cluster_free_from_rl - free clusters from runlist + * @vol: mounted ntfs volume on which to free the clusters + * @rl: runlist from which deallocate clusters + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) +{ + s64 nr_freed = 0; + int ret = -1; + + ntfs_log_trace("Entering.\n"); + + for (; rl->length; rl++) { + + ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", + (long long)rl->lcn, (long long)rl->length); + + if (rl->lcn >= 0) { + update_full_status(vol,rl->lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, + rl->length)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", + (long long)rl->lcn, + (long long)rl->length); + goto out; + } + nr_freed += rl->length ; + } + } + + ret = 0; +out: + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; +} + +/* + * Basic cluster run free + * Returns 0 if successful + */ + +int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count) +{ + s64 nr_freed = 0; + int ret = -1; + + ntfs_log_trace("Entering.\n"); + ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", + (long long)lcn, (long long)count); + + if (lcn >= 0) { + update_full_status(vol,lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, lcn, + count)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", + (long long)lcn, + (long long)count); + goto out; + } + nr_freed += count; + } + ret = 0; +out: + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; +} + +/** + * ntfs_cluster_free - free clusters on an ntfs volume + * @vol: mounted ntfs volume on which to free the clusters + * @na: attribute whose runlist describes the clusters to free + * @start_vcn: vcn in @rl at which to start freeing clusters + * @count: number of clusters to free or -1 for all clusters + * + * Free @count clusters starting at the cluster @start_vcn in the runlist + * described by the attribute @na from the mounted ntfs volume @vol. + * + * If @count is -1, all clusters from @start_vcn to the end of the runlist + * are deallocated. + * + * On success return the number of deallocated clusters (not counting sparse + * clusters) and on error return -1 with errno set to the error code. + */ +int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) +{ + runlist *rl; + s64 delta, to_free, nr_freed = 0; + int ret = -1; + + if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 || + (count < 0 && count != -1)) { + ntfs_log_trace("Invalid arguments!\n"); + errno = EINVAL; + return -1; + } + + ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " + "vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)count, (long long)start_vcn); + + rl = ntfs_attr_find_vcn(na, start_vcn); + if (!rl) { + if (errno == ENOENT) + ret = 0; + goto leave; + } + + if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { + errno = EIO; + ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__, + (long long)rl->lcn); + goto leave; + } + + /* Find the starting cluster inside the run that needs freeing. */ + delta = start_vcn - rl->vcn; + + /* The number of clusters in this run that need freeing. */ + to_free = rl->length - delta; + if (count >= 0 && to_free > count) + to_free = count; + + if (rl->lcn != LCN_HOLE) { + /* Do the actual freeing of the clusters in this run. */ + update_full_status(vol,rl->lcn + delta); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta, + to_free)) + goto leave; + nr_freed = to_free; + } + + /* Go to the next run and adjust the number of clusters left to free. */ + ++rl; + if (count >= 0) + count -= to_free; + + /* + * Loop over the remaining runs, using @count as a capping value, and + * free them. + */ + for (; rl->length && count != 0; ++rl) { + // FIXME: Need to try ntfs_attr_map_runlist() for attribute + // list support! (AIA) + if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { + // FIXME: Eeek! We need rollback! (AIA) + errno = EIO; + ntfs_log_perror("%s: Invalid lcn (%lli)", + __FUNCTION__, (long long)rl->lcn); + goto out; + } + + /* The number of clusters in this run that need freeing. */ + to_free = rl->length; + if (count >= 0 && to_free > count) + to_free = count; + + if (rl->lcn != LCN_HOLE) { + update_full_status(vol,rl->lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, + to_free)) { + // FIXME: Eeek! We need rollback! (AIA) + ntfs_log_perror("%s: Clearing bitmap run failed", + __FUNCTION__); + goto out; + } + nr_freed += to_free; + } + + if (count >= 0) + count -= to_free; + } + + if (count != -1 && count != 0) { + // FIXME: Eeek! BUG() + errno = EIO; + ntfs_log_perror("%s: count still not zero (%lld)", __FUNCTION__, + (long long)count); + goto out; + } + + ret = nr_freed; +out: + vol->free_clusters += nr_freed ; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); +leave: + ntfs_log_leave("\n"); + return ret; +} diff --git a/libntfs-3g/libntfs-3g.pc b/libntfs-3g/libntfs-3g.pc new file mode 100755 index 0000000000000000000000000000000000000000..637b3032e2926ad5d16d0ff670b54422f6c7127f --- /dev/null +++ b/libntfs-3g/libntfs-3g.pc @@ -0,0 +1,10 @@ +prefix=/usr/local +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libntfs-3g +Description: NTFS-3G Read/Write Driver Library +Version: 2015.3.14 +Cflags: -I${includedir} +Libs: -lpthread -L${libdir} -lntfs-3g diff --git a/libntfs-3g/libntfs-3g.pc.in b/libntfs-3g/libntfs-3g.pc.in new file mode 100755 index 0000000000000000000000000000000000000000..5f2fea73177c10e00ebee41d068a39dfdc0866b9 --- /dev/null +++ b/libntfs-3g/libntfs-3g.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libntfs-3g +Description: NTFS-3G Read/Write Driver Library +Version: @PACKAGE_VERSION@ +Cflags: -I${includedir} +Libs: @LIBFUSE_LITE_LIBS@ -L${libdir} -lntfs-3g diff --git a/libntfs-3g/libntfs-3g.script.so b/libntfs-3g/libntfs-3g.script.so new file mode 100755 index 0000000000000000000000000000000000000000..736d8eb05d23f2586f3e9ee1b1088c0495033669 --- /dev/null +++ b/libntfs-3g/libntfs-3g.script.so @@ -0,0 +1,2 @@ + +GROUP ( /lib/libntfs-3g.so ) diff --git a/libntfs-3g/libntfs-3g.script.so.in b/libntfs-3g/libntfs-3g.script.so.in new file mode 100755 index 0000000000000000000000000000000000000000..d050cfa3dd645a1fb6dd9c6f039fb4bf0a6e47d7 --- /dev/null +++ b/libntfs-3g/libntfs-3g.script.so.in @@ -0,0 +1,2 @@ +@OUTPUT_FORMAT@ +GROUP ( @rootlibdir@/libntfs-3g.so ) diff --git a/libntfs-3g/logfile.c b/libntfs-3g/logfile.c new file mode 100755 index 0000000000000000000000000000000000000000..336bdd282aa7e0b7a1ec806d69a85f07cf602659 --- /dev/null +++ b/libntfs-3g/logfile.c @@ -0,0 +1,744 @@ +/** + * logfile.c - NTFS journal handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2005-2009 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "attrib.h" +#include "debug.h" +#include "logfile.h" +#include "volume.h" +#include "mst.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_check_restart_page_header - check the page header for consistency + * @rp: restart page header to check + * @pos: position in logfile at which the restart page header resides + * + * Check the restart page header @rp for consistency and return TRUE if it is + * consistent and FALSE otherwise. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + */ +static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) +{ + u32 logfile_system_page_size, logfile_log_page_size; + u16 ra_ofs, usa_count, usa_ofs, usa_end = 0; + BOOL have_usa = TRUE; + + ntfs_log_trace("Entering.\n"); + /* + * If the system or log page sizes are smaller than the ntfs block size + * or either is not a power of 2 we cannot handle this log file. + */ + logfile_system_page_size = le32_to_cpu(rp->system_page_size); + logfile_log_page_size = le32_to_cpu(rp->log_page_size); + if (logfile_system_page_size < NTFS_BLOCK_SIZE || + logfile_log_page_size < NTFS_BLOCK_SIZE || + logfile_system_page_size & + (logfile_system_page_size - 1) || + logfile_log_page_size & (logfile_log_page_size - 1)) { + ntfs_log_error("$LogFile uses unsupported page size.\n"); + return FALSE; + } + /* + * We must be either at !pos (1st restart page) or at pos = system page + * size (2nd restart page). + */ + if (pos && pos != logfile_system_page_size) { + ntfs_log_error("Found restart area in incorrect " + "position in $LogFile.\n"); + return FALSE; + } + /* + * We only know how to handle version 1.1 and 2.0, though + * version 2.0 is probably related to cached metadata in + * Windows 8, and we will refuse to mount. + * Nevertheless, do all the relevant checks before rejecting. + */ + if (((rp->major_ver != const_cpu_to_le16(1)) + || (rp->minor_ver != const_cpu_to_le16(1))) + && ((rp->major_ver != const_cpu_to_le16(2)) + || (rp->minor_ver != const_cpu_to_le16(0)))) { + ntfs_log_error("$LogFile version %i.%i is not " + "supported.\n (This driver supports version " + "1.1 and 2.0 only.)\n", + (int)sle16_to_cpu(rp->major_ver), + (int)sle16_to_cpu(rp->minor_ver)); + return FALSE; + } + /* + * If chkdsk has been run the restart page may not be protected by an + * update sequence array. + */ + if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) { + have_usa = FALSE; + goto skip_usa_checks; + } + /* Verify the size of the update sequence array. */ + usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS); + if (usa_count != le16_to_cpu(rp->usa_count)) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent update sequence array count.\n"); + return FALSE; + } + /* Verify the position of the update sequence array. */ + usa_ofs = le16_to_cpu(rp->usa_ofs); + usa_end = usa_ofs + usa_count * sizeof(u16); + if (usa_ofs < sizeof(RESTART_PAGE_HEADER) || + usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent update sequence array offset.\n"); + return FALSE; + } +skip_usa_checks: + /* + * Verify the position of the restart area. It must be: + * - aligned to 8-byte boundary, + * - after the update sequence array, and + * - within the system page size. + */ + ra_ofs = le16_to_cpu(rp->restart_area_offset); + if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : + ra_ofs < sizeof(RESTART_PAGE_HEADER)) || + ra_ofs > logfile_system_page_size) { + ntfs_log_error("$LogFile restart page specifies " + "inconsistent restart area offset.\n"); + return FALSE; + } + /* + * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn + * set. + */ + if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { + ntfs_log_error("$LogFile restart page is not modified " + "by chkdsk but a chkdsk LSN is specified.\n"); + return FALSE; + } + ntfs_log_trace("Done.\n"); + return TRUE; +} + +/** + * ntfs_check_restart_area - check the restart area for consistency + * @rp: restart page whose restart area to check + * + * Check the restart area of the restart page @rp for consistency and return + * TRUE if it is consistent and FALSE otherwise. + * + * This function assumes that the restart page header has already been + * consistency checked. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + */ +static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp) +{ + u64 file_size; + RESTART_AREA *ra; + u16 ra_ofs, ra_len, ca_ofs; + u8 fs_bits; + + ntfs_log_trace("Entering.\n"); + ra_ofs = le16_to_cpu(rp->restart_area_offset); + ra = (RESTART_AREA*)((u8*)rp + ra_ofs); + /* + * Everything before ra->file_size must be before the first word + * protected by an update sequence number. This ensures that it is + * safe to access ra->client_array_offset. + */ + if (ra_ofs + offsetof(RESTART_AREA, file_size) > + NTFS_BLOCK_SIZE - sizeof(u16)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent file offset.\n"); + return FALSE; + } + /* + * Now that we can access ra->client_array_offset, make sure everything + * up to the log client array is before the first word protected by an + * update sequence number. This ensures we can access all of the + * restart area elements safely. Also, the client array offset must be + * aligned to an 8-byte boundary. + */ + ca_ofs = le16_to_cpu(ra->client_array_offset); + if (((ca_ofs + 7) & ~7) != ca_ofs || + ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE - + sizeof(u16))) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent client array offset.\n"); + return FALSE; + } + /* + * The restart area must end within the system page size both when + * calculated manually and as specified by ra->restart_area_length. + * Also, the calculated length must not exceed the specified length. + */ + ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * + sizeof(LOG_CLIENT_RECORD); + if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) || + (u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) > + le32_to_cpu(rp->system_page_size) || + ra_len > le16_to_cpu(ra->restart_area_length)) { + ntfs_log_error("$LogFile restart area is out of bounds " + "of the system page size specified by the " + "restart page header and/or the specified " + "restart area length is inconsistent.\n"); + return FALSE; + } + /* + * The ra->client_free_list and ra->client_in_use_list must be either + * LOGFILE_NO_CLIENT or less than ra->log_clients or they are + * overflowing the client array. + */ + if ((ra->client_free_list != LOGFILE_NO_CLIENT && + le16_to_cpu(ra->client_free_list) >= + le16_to_cpu(ra->log_clients)) || + (ra->client_in_use_list != LOGFILE_NO_CLIENT && + le16_to_cpu(ra->client_in_use_list) >= + le16_to_cpu(ra->log_clients))) { + ntfs_log_error("$LogFile restart area specifies " + "overflowing client free and/or in use lists.\n"); + return FALSE; + } + /* + * Check ra->seq_number_bits against ra->file_size for consistency. + * We cannot just use ffs() because the file size is not a power of 2. + */ + file_size = (u64)sle64_to_cpu(ra->file_size); + fs_bits = 0; + while (file_size) { + file_size >>= 1; + fs_bits++; + } + if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent sequence number bits.\n"); + return FALSE; + } + /* The log record header length must be a multiple of 8. */ + if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != + le16_to_cpu(ra->log_record_header_length)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent log record header length.\n"); + return FALSE; + } + /* Ditto for the log page data offset. */ + if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != + le16_to_cpu(ra->log_page_data_offset)) { + ntfs_log_error("$LogFile restart area specifies " + "inconsistent log page data offset.\n"); + return FALSE; + } + ntfs_log_trace("Done.\n"); + return TRUE; +} + +/** + * ntfs_check_log_client_array - check the log client array for consistency + * @rp: restart page whose log client array to check + * + * Check the log client array of the restart page @rp for consistency and + * return TRUE if it is consistent and FALSE otherwise. + * + * This function assumes that the restart page header and the restart area have + * already been consistency checked. + * + * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this + * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full + * restart page and the page must be multi sector transfer deprotected. + */ +static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp) +{ + RESTART_AREA *ra; + LOG_CLIENT_RECORD *ca, *cr; + u16 nr_clients, idx; + BOOL in_free_list, idx_is_first; + + ntfs_log_trace("Entering.\n"); + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + ca = (LOG_CLIENT_RECORD*)((u8*)ra + + le16_to_cpu(ra->client_array_offset)); + /* + * Check the ra->client_free_list first and then check the + * ra->client_in_use_list. Check each of the log client records in + * each of the lists and check that the array does not overflow the + * ra->log_clients value. Also keep track of the number of records + * visited as there cannot be more than ra->log_clients records and + * that way we detect eventual loops in within a list. + */ + nr_clients = le16_to_cpu(ra->log_clients); + idx = le16_to_cpu(ra->client_free_list); + in_free_list = TRUE; +check_list: + for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, + idx = le16_to_cpu(cr->next_client)) { + if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) + goto err_out; + /* Set @cr to the current log client record. */ + cr = ca + idx; + /* The first log client record must not have a prev_client. */ + if (idx_is_first) { + if (cr->prev_client != LOGFILE_NO_CLIENT) + goto err_out; + idx_is_first = FALSE; + } + } + /* Switch to and check the in use list if we just did the free list. */ + if (in_free_list) { + in_free_list = FALSE; + idx = le16_to_cpu(ra->client_in_use_list); + goto check_list; + } + ntfs_log_trace("Done.\n"); + return TRUE; +err_out: + ntfs_log_error("$LogFile log client array is corrupt.\n"); + return FALSE; +} + +/** + * ntfs_check_and_load_restart_page - check the restart page for consistency + * @log_na: opened ntfs attribute for journal $LogFile + * @rp: restart page to check + * @pos: position in @log_na at which the restart page resides + * @wrp: [OUT] copy of the multi sector transfer deprotected restart page + * @lsn: [OUT] set to the current logfile lsn on success + * + * Check the restart page @rp for consistency and return 0 if it is consistent + * and errno otherwise. The restart page may have been modified by chkdsk in + * which case its magic is CHKD instead of RSTR. + * + * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not + * require the full restart page. + * + * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a + * copy of the complete multi sector transfer deprotected page. On failure, + * *@wrp is undefined. + * + * Similarly, if @lsn is not NULL, on success *@lsn will be set to the current + * logfile lsn according to this restart page. On failure, *@lsn is undefined. + * + * The following error codes are defined: + * EINVAL - The restart page is inconsistent. + * ENOMEM - Not enough memory to load the restart page. + * EIO - Failed to reading from $LogFile. + */ +static int ntfs_check_and_load_restart_page(ntfs_attr *log_na, + RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, + LSN *lsn) +{ + RESTART_AREA *ra; + RESTART_PAGE_HEADER *trp; + int err; + + ntfs_log_trace("Entering.\n"); + /* Check the restart page header for consistency. */ + if (!ntfs_check_restart_page_header(rp, pos)) { + /* Error output already done inside the function. */ + return EINVAL; + } + /* Check the restart area for consistency. */ + if (!ntfs_check_restart_area(rp)) { + /* Error output already done inside the function. */ + return EINVAL; + } + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + /* + * Allocate a buffer to store the whole restart page so we can multi + * sector transfer deprotect it. + */ + trp = ntfs_malloc(le32_to_cpu(rp->system_page_size)); + if (!trp) + return errno; + /* + * Read the whole of the restart page into the buffer. If it fits + * completely inside @rp, just copy it from there. Otherwise read it + * from disk. + */ + if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE) + memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); + else if (ntfs_attr_pread(log_na, pos, + le32_to_cpu(rp->system_page_size), trp) != + le32_to_cpu(rp->system_page_size)) { + err = errno; + ntfs_log_error("Failed to read whole restart page into the " + "buffer.\n"); + if (err != ENOMEM) + err = EIO; + goto err_out; + } + /* + * Perform the multi sector transfer deprotection on the buffer if the + * restart page is protected. + */ + if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) + && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp, + le32_to_cpu(rp->system_page_size))) { + /* + * A multi sector tranfer error was detected. We only need to + * abort if the restart page contents exceed the multi sector + * transfer fixup of the first sector. + */ + if (le16_to_cpu(rp->restart_area_offset) + + le16_to_cpu(ra->restart_area_length) > + NTFS_BLOCK_SIZE - (int)sizeof(u16)) { + ntfs_log_error("Multi sector transfer error " + "detected in $LogFile restart page.\n"); + err = EINVAL; + goto err_out; + } + } + /* + * If the restart page is modified by chkdsk or there are no active + * logfile clients, the logfile is consistent. Otherwise, need to + * check the log client records for consistency, too. + */ + err = 0; + if (ntfs_is_rstr_record(rp->magic) && + ra->client_in_use_list != LOGFILE_NO_CLIENT) { + if (!ntfs_check_log_client_array(trp)) { + err = EINVAL; + goto err_out; + } + } + if (lsn) { + if (ntfs_is_rstr_record(rp->magic)) + *lsn = sle64_to_cpu(ra->current_lsn); + else /* if (ntfs_is_chkd_record(rp->magic)) */ + *lsn = sle64_to_cpu(rp->chkdsk_lsn); + } + ntfs_log_trace("Done.\n"); + if (wrp) + *wrp = trp; + else { +err_out: + free(trp); + } + return err; +} + +/** + * ntfs_check_logfile - check in the journal if the volume is consistent + * @log_na: ntfs attribute of loaded journal $LogFile to check + * @rp: [OUT] on success this is a copy of the current restart page + * + * Check the $LogFile journal for consistency and return TRUE if it is + * consistent and FALSE if not. On success, the current restart page is + * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it. + * + * At present we only check the two restart pages and ignore the log record + * pages. + * + * Note that the MstProtected flag is not set on the $LogFile inode and hence + * when reading pages they are not deprotected. This is because we do not know + * if the $LogFile was created on a system with a different page size to ours + * yet and mst deprotection would fail if our page size is smaller. + */ +BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) +{ + s64 size, pos; + LSN rstr1_lsn, rstr2_lsn; + ntfs_volume *vol = log_na->ni->vol; + u8 *kaddr = NULL; + RESTART_PAGE_HEADER *rstr1_ph = NULL; + RESTART_PAGE_HEADER *rstr2_ph = NULL; + int log_page_size, err; + BOOL logfile_is_empty = TRUE; + u8 log_page_bits; + + ntfs_log_trace("Entering.\n"); + /* An empty $LogFile must have been clean before it got emptied. */ + if (NVolLogFileEmpty(vol)) + goto is_empty; + size = log_na->data_size; + /* Make sure the file doesn't exceed the maximum allowed size. */ + if (size > (s64)MaxLogFileSize) + size = MaxLogFileSize; + log_page_size = DefaultLogPageSize; + /* + * Use generic_ffs() instead of ffs() to enable the compiler to + * optimize log_page_size and log_page_bits into constants. + */ + log_page_bits = ffs(log_page_size) - 1; + size &= ~(log_page_size - 1); + + /* + * Ensure the log file is big enough to store at least the two restart + * pages and the minimum number of log record pages. + */ + if (size < log_page_size * 2 || (size - log_page_size * 2) >> + log_page_bits < MinLogRecordPages) { + ntfs_log_error("$LogFile is too small.\n"); + return FALSE; + } + /* Allocate memory for restart page. */ + kaddr = ntfs_malloc(NTFS_BLOCK_SIZE); + if (!kaddr) + return FALSE; + /* + * Read through the file looking for a restart page. Since the restart + * page header is at the beginning of a page we only need to search at + * what could be the beginning of a page (for each page size) rather + * than scanning the whole file byte by byte. If all potential places + * contain empty and uninitialized records, the log file can be assumed + * to be empty. + */ + for (pos = 0; pos < size; pos <<= 1) { + /* + * Read first NTFS_BLOCK_SIZE bytes of potential restart page. + */ + if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) != + NTFS_BLOCK_SIZE) { + ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE " + "bytes of potential restart page.\n"); + goto err_out; + } + + /* + * A non-empty block means the logfile is not empty while an + * empty block after a non-empty block has been encountered + * means we are done. + */ + if (!ntfs_is_empty_recordp((le32*)kaddr)) + logfile_is_empty = FALSE; + else if (!logfile_is_empty) + break; + /* + * A log record page means there cannot be a restart page after + * this so no need to continue searching. + */ + if (ntfs_is_rcrd_recordp((le32*)kaddr)) + break; + /* If not a (modified by chkdsk) restart page, continue. */ + if (!ntfs_is_rstr_recordp((le32*)kaddr) && + !ntfs_is_chkd_recordp((le32*)kaddr)) { + if (!pos) + pos = NTFS_BLOCK_SIZE >> 1; + continue; + } + /* + * Check the (modified by chkdsk) restart page for consistency + * and get a copy of the complete multi sector transfer + * deprotected restart page. + */ + err = ntfs_check_and_load_restart_page(log_na, + (RESTART_PAGE_HEADER*)kaddr, pos, + !rstr1_ph ? &rstr1_ph : &rstr2_ph, + !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); + if (!err) { + /* + * If we have now found the first (modified by chkdsk) + * restart page, continue looking for the second one. + */ + if (!pos) { + pos = NTFS_BLOCK_SIZE >> 1; + continue; + } + /* + * We have now found the second (modified by chkdsk) + * restart page, so we can stop looking. + */ + break; + } + /* + * Error output already done inside the function. Note, we do + * not abort if the restart page was invalid as we might still + * find a valid one further in the file. + */ + if (err != EINVAL) + goto err_out; + /* Continue looking. */ + if (!pos) + pos = NTFS_BLOCK_SIZE >> 1; + } + if (kaddr) { + free(kaddr); + kaddr = NULL; + } + if (logfile_is_empty) { + NVolSetLogFileEmpty(vol); +is_empty: + ntfs_log_trace("Done. ($LogFile is empty.)\n"); + return TRUE; + } + if (!rstr1_ph) { + if (rstr2_ph) + ntfs_log_error("BUG: rstr2_ph isn't NULL!\n"); + ntfs_log_error("Did not find any restart pages in " + "$LogFile and it was not empty.\n"); + return FALSE; + } + /* If both restart pages were found, use the more recent one. */ + if (rstr2_ph) { + /* + * If the second restart area is more recent, switch to it. + * Otherwise just throw it away. + */ + if (rstr2_lsn > rstr1_lsn) { + ntfs_log_debug("Using second restart page as it is more " + "recent.\n"); + free(rstr1_ph); + rstr1_ph = rstr2_ph; + /* rstr1_lsn = rstr2_lsn; */ + } else { + ntfs_log_debug("Using first restart page as it is more " + "recent.\n"); + free(rstr2_ph); + } + rstr2_ph = NULL; + } + /* All consistency checks passed. */ + if (rp) + *rp = rstr1_ph; + else + free(rstr1_ph); + ntfs_log_trace("Done.\n"); + return TRUE; +err_out: + free(kaddr); + free(rstr1_ph); + free(rstr2_ph); + return FALSE; +} + +/** + * ntfs_is_logfile_clean - check in the journal if the volume is clean + * @log_na: ntfs attribute of loaded journal $LogFile to check + * @rp: copy of the current restart page + * + * Analyze the $LogFile journal and return TRUE if it indicates the volume was + * shutdown cleanly and FALSE if not. + * + * At present we only look at the two restart pages and ignore the log record + * pages. This is a little bit crude in that there will be a very small number + * of cases where we think that a volume is dirty when in fact it is clean. + * This should only affect volumes that have not been shutdown cleanly but did + * not have any pending, non-check-pointed i/o, i.e. they were completely idle + * at least for the five seconds preceding the unclean shutdown. + * + * This function assumes that the $LogFile journal has already been consistency + * checked by a call to ntfs_check_logfile() and in particular if the $LogFile + * is empty this function requires that NVolLogFileEmpty() is true otherwise an + * empty volume will be reported as dirty. + */ +BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) +{ + RESTART_AREA *ra; + + ntfs_log_trace("Entering.\n"); + /* An empty $LogFile must have been clean before it got emptied. */ + if (NVolLogFileEmpty(log_na->ni->vol)) { + ntfs_log_trace("$LogFile is empty\n"); + return TRUE; + } + if (!rp) { + ntfs_log_error("Restart page header is NULL\n"); + return FALSE; + } + if (!ntfs_is_rstr_record(rp->magic) && + !ntfs_is_chkd_record(rp->magic)) { + ntfs_log_error("Restart page buffer is invalid\n"); + return FALSE; + } + + ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); + /* + * If the $LogFile has active clients, i.e. it is open, and we do not + * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, + * we assume there was an unclean shutdown. + */ + if (ra->client_in_use_list != LOGFILE_NO_CLIENT && + !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { + ntfs_log_error("The disk contains an unclean file system (%d, " + "%d).\n", le16_to_cpu(ra->client_in_use_list), + le16_to_cpu(ra->flags)); + return FALSE; + } + /* $LogFile indicates a clean shutdown. */ + ntfs_log_trace("$LogFile indicates a clean shutdown\n"); + return TRUE; +} + +/** + * ntfs_empty_logfile - empty the contents of the $LogFile journal + * @na: ntfs attribute of journal $LogFile to empty + * + * Empty the contents of the $LogFile journal @na and return 0 on success and + * -1 on error. + * + * This function assumes that the $LogFile journal has already been consistency + * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() + * has been used to ensure that the $LogFile is clean. + */ +int ntfs_empty_logfile(ntfs_attr *na) +{ + s64 pos, count; + char buf[NTFS_BUF_SIZE]; + + ntfs_log_trace("Entering.\n"); + + if (NVolLogFileEmpty(na->ni->vol)) + return 0; + + if (!NAttrNonResident(na)) { + errno = EIO; + ntfs_log_perror("Resident $LogFile $DATA attribute"); + return -1; + } + + memset(buf, -1, NTFS_BUF_SIZE); + + pos = 0; + while ((count = na->data_size - pos) > 0) { + + if (count > NTFS_BUF_SIZE) + count = NTFS_BUF_SIZE; + + count = ntfs_attr_pwrite(na, pos, count, buf); + if (count <= 0) { + ntfs_log_perror("Failed to reset $LogFile"); + if (count != -1) + errno = EIO; + return -1; + } + pos += count; + } + + NVolSetLogFileEmpty(na->ni->vol); + + return 0; +} diff --git a/libntfs-3g/logging.c b/libntfs-3g/logging.c new file mode 100755 index 0000000000000000000000000000000000000000..8f3d7bc5434fdcac013016707c9d7c022da6177f --- /dev/null +++ b/libntfs-3g/logging.c @@ -0,0 +1,661 @@ +/** + * logging.c - Centralised logging. Originated from the Linux-NTFS project. + * + * Copyright (c) 2005 Richard Russon + * Copyright (c) 2005-2008 Szabolcs Szakacsits + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#define LOG_TAG "NTFS-3G" + +#include <utils/Log.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif + +#include "logging.h" +#include "misc.h" + +#ifndef PATH_SEP +#define PATH_SEP '/' +#endif + +#ifdef DEBUG +static int tab; +#endif + +/* Some gcc 3.x, 4.[01].X crash with internal compiler error. */ +#if __GNUC__ <= 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 1) +# define BROKEN_GCC_FORMAT_ATTRIBUTE +#else +# define BROKEN_GCC_FORMAT_ATTRIBUTE __attribute__((format(printf, 6, 0))) +#endif + +#if PLATFORM_SDK_VERSION >= 16 +#define LOGV(fmt,args...) ALOGV(fmt,##args) +#define LOGD(fmt,args...) ALOGD(fmt,##args) +#define LOGI(fmt,args...) ALOGI(fmt,##args) +#define LOGW(fmt,args...) ALOGW(fmt,##args) +#define LOGE(fmt,args...) ALOGE(fmt,##args) +#endif + +/** + * struct ntfs_logging - Control info for the logging system + * @levels: Bitfield of logging levels + * @flags: Flags which affect the output style + * @handler: Function to perform the actual logging + */ +struct ntfs_logging { + u32 levels; + u32 flags; + ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE; +}; + +/** + * ntfs_log + * This struct controls all the logging within the library and tools. + */ +static struct ntfs_logging ntfs_log = { +#ifdef DEBUG + NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER | + NTFS_LOG_LEVEL_LEAVE | +#endif + NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING | + NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL | + NTFS_LOG_LEVEL_PROGRESS, + NTFS_LOG_FLAG_ONLYNAME, +#ifdef DEBUG + ntfs_log_handler_outerr +#else + ntfs_log_handler_null +#endif +}; + + +/** + * ntfs_log_get_levels - Get a list of the current logging levels + * + * Find out which logging levels are enabled. + * + * Returns: Log levels in a 32-bit field + */ +u32 ntfs_log_get_levels(void) +{ + return ntfs_log.levels; +} + +/** + * ntfs_log_set_levels - Enable extra logging levels + * @levels: 32-bit field of log levels to set + * + * Enable one or more logging levels. + * The logging levels are named: NTFS_LOG_LEVEL_*. + * + * Returns: Log levels that were enabled before the call + */ +u32 ntfs_log_set_levels(u32 levels) +{ + u32 old; + old = ntfs_log.levels; + ntfs_log.levels |= levels; + return old; +} + +/** + * ntfs_log_clear_levels - Disable some logging levels + * @levels: 32-bit field of log levels to clear + * + * Disable one or more logging levels. + * The logging levels are named: NTFS_LOG_LEVEL_*. + * + * Returns: Log levels that were enabled before the call + */ +u32 ntfs_log_clear_levels(u32 levels) +{ + u32 old; + old = ntfs_log.levels; + ntfs_log.levels &= (~levels); + return old; +} + + +/** + * ntfs_log_get_flags - Get a list of logging style flags + * + * Find out which logging flags are enabled. + * + * Returns: Logging flags in a 32-bit field + */ +u32 ntfs_log_get_flags(void) +{ + return ntfs_log.flags; +} + +/** + * ntfs_log_set_flags - Enable extra logging style flags + * @flags: 32-bit field of logging flags to set + * + * Enable one or more logging flags. + * The log flags are named: NTFS_LOG_LEVEL_*. + * + * Returns: Logging flags that were enabled before the call + */ +u32 ntfs_log_set_flags(u32 flags) +{ + u32 old; + old = ntfs_log.flags; + ntfs_log.flags |= flags; + return old; +} + +/** + * ntfs_log_clear_flags - Disable some logging styles + * @flags: 32-bit field of logging flags to clear + * + * Disable one or more logging flags. + * The log flags are named: NTFS_LOG_LEVEL_*. + * + * Returns: Logging flags that were enabled before the call + */ +u32 ntfs_log_clear_flags(u32 flags) +{ + u32 old; + old = ntfs_log.flags; + ntfs_log.flags &= (~flags); + return old; +} + + +/** + * ntfs_log_get_stream - Default output streams for logging levels + * @level: Log level + * + * By default, urgent messages are sent to "stderr". + * Other messages are sent to "stdout". + * + * Returns: "string" Prefix to be used + */ +static FILE * ntfs_log_get_stream(u32 level) +{ + FILE *stream; + + switch (level) { + case NTFS_LOG_LEVEL_INFO: + case NTFS_LOG_LEVEL_QUIET: + case NTFS_LOG_LEVEL_PROGRESS: + case NTFS_LOG_LEVEL_VERBOSE: + stream = stdout; + break; + + case NTFS_LOG_LEVEL_DEBUG: + case NTFS_LOG_LEVEL_TRACE: + case NTFS_LOG_LEVEL_ENTER: + case NTFS_LOG_LEVEL_LEAVE: + case NTFS_LOG_LEVEL_WARNING: + case NTFS_LOG_LEVEL_ERROR: + case NTFS_LOG_LEVEL_CRITICAL: + case NTFS_LOG_LEVEL_PERROR: + default: + stream = stderr; + break; + } + + return stream; +} + +/** + * ntfs_log_get_prefix - Default prefixes for logging levels + * @level: Log level to be prefixed + * + * Prefixing the logging output can make it easier to parse. + * + * Returns: "string" Prefix to be used + */ +static const char * ntfs_log_get_prefix(u32 level) +{ + const char *prefix; + + switch (level) { + case NTFS_LOG_LEVEL_DEBUG: + prefix = "DEBUG: "; + break; + case NTFS_LOG_LEVEL_TRACE: + prefix = "TRACE: "; + break; + case NTFS_LOG_LEVEL_QUIET: + prefix = "QUIET: "; + break; + case NTFS_LOG_LEVEL_INFO: + prefix = "INFO: "; + break; + case NTFS_LOG_LEVEL_VERBOSE: + prefix = "VERBOSE: "; + break; + case NTFS_LOG_LEVEL_PROGRESS: + prefix = "PROGRESS: "; + break; + case NTFS_LOG_LEVEL_WARNING: + prefix = "WARNING: "; + break; + case NTFS_LOG_LEVEL_ERROR: + prefix = "ERROR: "; + break; + case NTFS_LOG_LEVEL_PERROR: + prefix = "ERROR: "; + break; + case NTFS_LOG_LEVEL_CRITICAL: + prefix = "CRITICAL: "; + break; + default: + prefix = ""; + break; + } + + return prefix; +} + + +/** + * ntfs_log_set_handler - Provide an alternate logging handler + * @handler: function to perform the logging + * + * This alternate handler will be called for all future logging requests. + * If no @handler is specified, logging will revert to the default handler. + */ +void ntfs_log_set_handler(ntfs_log_handler *handler) +{ + if (handler) { + ntfs_log.handler = handler; +#ifdef HAVE_SYSLOG_H + if (handler == ntfs_log_handler_syslog) + openlog("ntfs-3g", LOG_PID, LOG_USER); +#endif + } else + ntfs_log.handler = ntfs_log_handler_null; +} + +/** + * ntfs_log_redirect - Pass on the request to the real handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @...: Arguments to be formatted + * + * This is just a redirector function. The arguments are simply passed to the + * main logging handler (as defined in the global logging struct @ntfs_log). + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_redirect(const char *function, const char *file, + int line, u32 level, void *data, const char *format, ...) +{ + #if 1 + va_list args; + char szBuf[512]; + + va_start(args, format); + vsnprintf(szBuf, sizeof(szBuf)/2, format, args); + va_end(args); + snprintf(&(szBuf[sizeof(szBuf)/2]), sizeof(szBuf)/2, + ", file: %s, function: %s, line: %d", file, function, line); + __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, szBuf); + return 0; + #else + int olderr = errno; + int ret; + va_list args; + + if (!(ntfs_log.levels & level)) /* Don't log this message */ + return 0; + + va_start(args, format); + errno = olderr; + ret = ntfs_log.handler(function, file, line, level, data, format, args); + va_end(args); + + errno = olderr; + return ret; + #endif +} + + +/** + * ntfs_log_handler_syslog - syslog logging handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * A simple syslog logging handler. Ignores colors. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ + + +#ifdef HAVE_SYSLOG_H + +#define LOG_LINE_LEN 512 + +int ntfs_log_handler_syslog(const char *function __attribute__((unused)), + const char *file __attribute__((unused)), + int line __attribute__((unused)), u32 level, + void *data __attribute__((unused)), + const char *format, va_list args) +{ + char logbuf[LOG_LINE_LEN]; + int ret, olderr = errno; + +#ifndef DEBUG + if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC) + return 1; +#endif + ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args); + if (ret < 0) { + vsyslog(LOG_NOTICE, format, args); + ret = 1; + goto out; + } + + if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) { + strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1); + strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3)); + ret = strlen(logbuf); + } + + syslog(LOG_NOTICE, "%s", logbuf); +out: + errno = olderr; + return ret; +} +#endif + +/* + * Early logging before the logs are redirected + * + * (not quite satisfactory : this appears before the ntfs-g banner, + * and with a different pid) + */ + +void ntfs_log_early_error(const char *format, ...) +{ + va_list args; + + va_start(args, format); +#ifdef HAVE_SYSLOG_H + openlog("ntfs-3g", LOG_PID, LOG_USER); + ntfs_log_handler_syslog(NULL, NULL, 0, + NTFS_LOG_LEVEL_ERROR, NULL, + format, args); +#else + vfprintf(stderr,format,args); +#endif + va_end(args); +} + +/** + * ntfs_log_handler_fprintf - Basic logging handler + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * A simple logging handler. This is where the log line is finally displayed. + * It is more likely that you will want to set the handler to either + * ntfs_log_handler_outerr or ntfs_log_handler_stderr. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, nothing will be displayed. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_fprintf(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ +#ifdef DEBUG + int i; +#endif + int ret = 0; + int olderr = errno; + FILE *stream; + + if (!data) /* Interpret data as a FILE stream. */ + return 0; /* If it's NULL, we can't do anything. */ + stream = (FILE*)data; + +#ifdef DEBUG + if (level == NTFS_LOG_LEVEL_LEAVE) { + if (tab) + tab--; + return 0; + } + + for (i = 0; i < tab; i++) + ret += fprintf(stream, " "); +#endif + if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && + (strchr(file, PATH_SEP))) /* Abbreviate the filename */ + file = strrchr(file, PATH_SEP) + 1; + + if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */ + ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); + + if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */ + ret += fprintf(stream, "%s ", file); + + if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */ + ret += fprintf(stream, "(%d) ", line); + + if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ + (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER)) + ret += fprintf(stream, "%s(): ", function); + + ret += vfprintf(stream, format, args); + + if (level & NTFS_LOG_LEVEL_PERROR) + ret += fprintf(stream, ": %s\n", strerror(olderr)); + +#ifdef DEBUG + if (level == NTFS_LOG_LEVEL_ENTER) + tab++; +#endif + fflush(stream); + errno = olderr; + return ret; +} + +/** + * ntfs_log_handler_null - Null logging handler (no output) + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * This handler produces no output. It provides a way to temporarily disable + * logging, without having to change the levels and flags. + * + * Returns: 0 Message wasn't logged + */ +int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)), + int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)), + const char *format __attribute__((unused)), va_list args __attribute__((unused))) +{ + return 0; +} + +/** + * ntfs_log_handler_stdout - All logs go to stdout + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * Display a log message to stdout. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, then stdout will be used. + * + * Note: This function calls ntfs_log_handler_fprintf to do the main work. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_stdout(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ + if (!data) + data = stdout; + + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); +} + +/** + * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * Display a log message. The output stream will be determined by the log + * level. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, the function ntfs_log_get_stream will be called + * + * Note: This function calls ntfs_log_handler_fprintf to do the main work. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_outerr(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ + if (!data) + data = ntfs_log_get_stream(level); + + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); +} + +/** + * ntfs_log_handler_stderr - All logs go to stderr + * @function: Function in which the log line occurred + * @file: File in which the log line occurred + * @line: Line number on which the log line occurred + * @level: Level at which the line is logged + * @data: User specified data, possibly specific to a handler + * @format: printf-style formatting string + * @args: Arguments to be formatted + * + * Display a log message to stderr. + * + * Note: For this handler, @data is a pointer to a FILE output stream. + * If @data is NULL, then stdout will be used. + * + * Note: This function calls ntfs_log_handler_fprintf to do the main work. + * + * Returns: -1 Error occurred + * 0 Message wasn't logged + * num Number of output characters + */ +int ntfs_log_handler_stderr(const char *function, const char *file, + int line, u32 level, void *data, const char *format, va_list args) +{ + if (!data) + data = stderr; + + return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); +} + + +/** + * ntfs_log_parse_option - Act upon command line options + * @option: Option flag + * + * Delegate some of the work of parsing the command line. All the options begin + * with "--log-". Options cause log levels to be enabled in @ntfs_log (the + * global logging structure). + * + * Note: The "colour" option changes the logging handler. + * + * Returns: TRUE Option understood + * FALSE Invalid log option + */ +BOOL ntfs_log_parse_option(const char *option) +{ + if (strcmp(option, "--log-debug") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); + return TRUE; + } else if (strcmp(option, "--log-verbose") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + return TRUE; + } else if (strcmp(option, "--log-quiet") == 0) { + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + return TRUE; + } else if (strcmp(option, "--log-trace") == 0) { + ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); + return TRUE; + } + + ntfs_log_debug("Unknown logging option '%s'\n", option); + return FALSE; +} + diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c new file mode 100755 index 0000000000000000000000000000000000000000..ac4c610b5e5b0ff1a0bb4fd6fe15b2d89a119f67 --- /dev/null +++ b/libntfs-3g/mft.c @@ -0,0 +1,1946 @@ +/** + * mft.c - Mft record handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2004-2008 Szabolcs Szakacsits + * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2014 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#include <time.h> + +#include "compat.h" +#include "types.h" +#include "device.h" +#include "debug.h" +#include "bitmap.h" +#include "attrib.h" +#include "inode.h" +#include "volume.h" +#include "layout.h" +#include "lcnalloc.h" +#include "mft.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_mft_records_read - read records from the mft from disk + * @vol: volume to read from + * @mref: starting mft record number to read + * @count: number of mft records to read + * @b: output data buffer + * + * Read @count mft records starting at @mref from volume @vol into buffer + * @b. Return 0 on success or -1 on error, with errno set to the error + * code. + * + * If any of the records exceed the initialized size of the $MFT/$DATA + * attribute, i.e. they cannot possibly be allocated mft records, assume this + * is a bug and return error code ESPIPE. + * + * The read mft records are mst deprotected and are hence ready to use. The + * caller should check each record with is_baad_record() in case mst + * deprotection failed. + * + * NOTE: @b has to be at least of size @count * vol->mft_record_size. + */ +int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b) +{ + s64 br; + VCN m; + + ntfs_log_trace("inode %llu\n", (unsigned long long)MREF(mref)); + + if (!vol || !vol->mft_na || !b || count < 0) { + errno = EINVAL; + ntfs_log_perror("%s: b=%p count=%lld mft=%llu", __FUNCTION__, + b, (long long)count, (unsigned long long)MREF(mref)); + return -1; + } + m = MREF(mref); + /* Refuse to read non-allocated mft records. */ + if (m + count > vol->mft_na->initialized_size >> + vol->mft_record_size_bits) { + errno = ESPIPE; + ntfs_log_perror("Trying to read non-allocated mft records " + "(%lld > %lld)", (long long)m + count, + (long long)vol->mft_na->initialized_size >> + vol->mft_record_size_bits); + return -1; + } + br = ntfs_attr_mst_pread(vol->mft_na, m << vol->mft_record_size_bits, + count, vol->mft_record_size, b); + if (br != count) { + if (br != -1) + errno = EIO; + ntfs_log_perror("Failed to read of MFT, mft=%llu count=%lld " + "br=%lld", (long long)m, (long long)count, + (long long)br); + return -1; + } + return 0; +} + +/** + * ntfs_mft_records_write - write mft records to disk + * @vol: volume to write to + * @mref: starting mft record number to write + * @count: number of mft records to write + * @b: data buffer containing the mft records to write + * + * Write @count mft records starting at @mref from data buffer @b to volume + * @vol. Return 0 on success or -1 on error, with errno set to the error code. + * + * If any of the records exceed the initialized size of the $MFT/$DATA + * attribute, i.e. they cannot possibly be allocated mft records, assume this + * is a bug and return error code ESPIPE. + * + * Before the mft records are written, they are mst protected. After the write, + * they are deprotected again, thus resulting in an increase in the update + * sequence number inside the data buffer @b. + * + * If any mft records are written which are also represented in the mft mirror + * $MFTMirr, we make a copy of the relevant parts of the data buffer @b into a + * temporary buffer before we do the actual write. Then if at least one mft + * record was successfully written, we write the appropriate mft records from + * the copied buffer to the mft mirror, too. + */ +int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, + const s64 count, MFT_RECORD *b) +{ + s64 bw; + VCN m; + void *bmirr = NULL; + int cnt = 0, res = 0; + + if (!vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0) { + errno = EINVAL; + return -1; + } + m = MREF(mref); + /* Refuse to write non-allocated mft records. */ + if (m + count > vol->mft_na->initialized_size >> + vol->mft_record_size_bits) { + errno = ESPIPE; + ntfs_log_perror("Trying to write non-allocated mft records " + "(%lld > %lld)", (long long)m + count, + (long long)vol->mft_na->initialized_size >> + vol->mft_record_size_bits); + return -1; + } + if (m < vol->mftmirr_size) { + if (!vol->mftmirr_na) { + errno = EINVAL; + return -1; + } + cnt = vol->mftmirr_size - m; + if (cnt > count) + cnt = count; + bmirr = ntfs_malloc(cnt * vol->mft_record_size); + if (!bmirr) + return -1; + memcpy(bmirr, b, cnt * vol->mft_record_size); + } + bw = ntfs_attr_mst_pwrite(vol->mft_na, m << vol->mft_record_size_bits, + count, vol->mft_record_size, b); + if (bw != count) { + if (bw != -1) + errno = EIO; + if (bw >= 0) + ntfs_log_debug("Error: partial write while writing $Mft " + "record(s)!\n"); + else + ntfs_log_perror("Error writing $Mft record(s)"); + res = errno; + } + if (bmirr && bw > 0) { + if (bw < cnt) + cnt = bw; + bw = ntfs_attr_mst_pwrite(vol->mftmirr_na, + m << vol->mft_record_size_bits, cnt, + vol->mft_record_size, bmirr); + if (bw != cnt) { + if (bw != -1) + errno = EIO; + ntfs_log_debug("Error: failed to sync $MFTMirr! Run " + "chkdsk.\n"); + res = errno; + } + } + free(bmirr); + if (!res) + return res; + errno = res; + return -1; +} + +int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m) +{ + ATTR_RECORD *a; + int ret = -1; + + if (!ntfs_is_file_record(m->magic)) { + if (!NVolNoFixupWarn(vol)) + ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", + (unsigned long long)MREF(mref), + (int)le32_to_cpu(*(le32*)m)); + goto err_out; + } + + if (le32_to_cpu(m->bytes_allocated) != vol->mft_record_size) { + ntfs_log_error("Record %llu has corrupt allocation size " + "(%u <> %u)\n", (unsigned long long)MREF(mref), + vol->mft_record_size, + le32_to_cpu(m->bytes_allocated)); + goto err_out; + } + + a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset)); + if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) { + ntfs_log_error("Record %llu is corrupt\n", + (unsigned long long)MREF(mref)); + goto err_out; + } + + ret = 0; +err_out: + if (ret) + errno = EIO; + return ret; +} + +/** + * ntfs_file_record_read - read a FILE record from the mft from disk + * @vol: volume to read from + * @mref: mft reference specifying mft record to read + * @mrec: address of pointer in which to return the mft record + * @attr: address of pointer in which to return the first attribute + * + * Read a FILE record from the mft of @vol from the storage medium. @mref + * specifies the mft record to read, including the sequence number, which can + * be 0 if no sequence number checking is to be performed. + * + * The function allocates a buffer large enough to hold the mft record and + * reads the record into the buffer (mst deprotecting it in the process). + * *@mrec is then set to point to the buffer. + * + * If @attr is not NULL, *@attr is set to point to the first attribute in the + * mft record, i.e. *@attr is a pointer into *@mrec. + * + * Return 0 on success, or -1 on error, with errno set to the error code. + * + * The read mft record is checked for having the magic FILE, + * and for having a matching sequence number (if MSEQNO(*@mref) != 0). + * If either of these fails, -1 is returned and errno is set to EIO. If you get + * this, but you still want to read the mft record (e.g. in order to correct + * it), use ntfs_mft_record_read() directly. + * + * Note: Caller has to free *@mrec when finished. + * + * Note: We do not check if the mft record is flagged in use. The caller can + * check if desired. + */ +int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD **mrec, ATTR_RECORD **attr) +{ + MFT_RECORD *m; + + if (!vol || !mrec) { + errno = EINVAL; + ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); + return -1; + } + + m = *mrec; + if (!m) { + m = ntfs_malloc(vol->mft_record_size); + if (!m) + return -1; + } + if (ntfs_mft_record_read(vol, mref, m)) + goto err_out; + + if (ntfs_mft_record_check(vol, mref, m)) + goto err_out; + + if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) { + ntfs_log_error("Record %llu has wrong SeqNo (%d <> %d)\n", + (unsigned long long)MREF(mref), MSEQNO(mref), + le16_to_cpu(m->sequence_number)); + errno = EIO; + goto err_out; + } + *mrec = m; + if (attr) + *attr = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); + return 0; +err_out: + if (m != *mrec) + free(m); + return -1; +} + +/** + * ntfs_mft_record_layout - layout an mft record into a memory buffer + * @vol: volume to which the mft record will belong + * @mref: mft reference specifying the mft record number + * @mrec: destination buffer of size >= @vol->mft_record_size bytes + * + * Layout an empty, unused mft record with the mft reference @mref into the + * buffer @m. The volume @vol is needed because the mft record structure was + * modified in NTFS 3.1 so we need to know which volume version this mft record + * will be used on. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *mrec) +{ + ATTR_RECORD *a; + + if (!vol || !mrec) { + errno = EINVAL; + ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); + return -1; + } + /* Aligned to 2-byte boundary. */ + if (vol->major_ver < 3 || (vol->major_ver == 3 && !vol->minor_ver)) + mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD_OLD) + 1) & ~1); + else { + /* Abort if mref is > 32 bits. */ + if (MREF(mref) & 0x0000ffff00000000ull) { + errno = ERANGE; + ntfs_log_perror("Mft reference exceeds 32 bits"); + return -1; + } + mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1); + /* + * Set the NTFS 3.1+ specific fields while we know that the + * volume version is 3.1+. + */ + mrec->reserved = cpu_to_le16(0); + mrec->mft_record_number = cpu_to_le32(MREF(mref)); + } + mrec->magic = magic_FILE; + if (vol->mft_record_size >= NTFS_BLOCK_SIZE) + mrec->usa_count = cpu_to_le16(vol->mft_record_size / + NTFS_BLOCK_SIZE + 1); + else { + mrec->usa_count = cpu_to_le16(1); + ntfs_log_error("Sector size is bigger than MFT record size. " + "Setting usa_count to 1. If Windows chkdsk " + "reports this as corruption, please email %s " + "stating that you saw this message and that " + "the file system created was corrupt. " + "Thank you.\n", NTFS_DEV_LIST); + } + /* Set the update sequence number to 1. */ + *(u16*)((u8*)mrec + le16_to_cpu(mrec->usa_ofs)) = cpu_to_le16(1); + mrec->lsn = cpu_to_le64(0ull); + mrec->sequence_number = cpu_to_le16(1); + mrec->link_count = cpu_to_le16(0); + /* Aligned to 8-byte boundary. */ + mrec->attrs_offset = cpu_to_le16((le16_to_cpu(mrec->usa_ofs) + + (le16_to_cpu(mrec->usa_count) << 1) + 7) & ~7); + mrec->flags = cpu_to_le16(0); + /* + * Using attrs_offset plus eight bytes (for the termination attribute), + * aligned to 8-byte boundary. + */ + mrec->bytes_in_use = cpu_to_le32((le16_to_cpu(mrec->attrs_offset) + 8 + + 7) & ~7); + mrec->bytes_allocated = cpu_to_le32(vol->mft_record_size); + mrec->base_mft_record = cpu_to_le64((MFT_REF)0); + mrec->next_attr_instance = cpu_to_le16(0); + a = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); + a->type = AT_END; + a->length = cpu_to_le32(0); + /* Finally, clear the unused part of the mft record. */ + memset((u8*)a + 8, 0, vol->mft_record_size - ((u8*)a + 8 - (u8*)mrec)); + return 0; +} + +/** + * ntfs_mft_record_format - format an mft record on an ntfs volume + * @vol: volume on which to format the mft record + * @mref: mft reference specifying mft record to format + * + * Format the mft record with the mft reference @mref in $MFT/$DATA, i.e. lay + * out an empty, unused mft record in memory and write it to the volume @vol. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref) +{ + MFT_RECORD *m; + int ret = -1; + + ntfs_log_enter("Entering\n"); + + m = ntfs_calloc(vol->mft_record_size); + if (!m) + goto out; + + if (ntfs_mft_record_layout(vol, mref, m)) + goto free_m; + + if (ntfs_mft_record_write(vol, mref, m)) + goto free_m; + + ret = 0; +free_m: + free(m); +out: + ntfs_log_leave("\n"); + return ret; +} + +static const char *es = " Leaving inconsistent metadata. Run chkdsk."; + +/** + * ntfs_ffz - Find the first unset (zero) bit in a word + * @word: + * + * Description... + * + * Returns: + */ +static inline unsigned int ntfs_ffz(unsigned int word) +{ + return ffs(~word) - 1; +} + +static int ntfs_is_mft(ntfs_inode *ni) +{ + if (ni && ni->mft_no == FILE_MFT) + return 1; + return 0; +} + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +#define RESERVED_MFT_RECORDS 64 + +/** + * ntfs_mft_bitmap_find_free_rec - find a free mft record in the mft bitmap + * @vol: volume on which to search for a free mft record + * @base_ni: open base inode if allocating an extent mft record or NULL + * + * Search for a free mft record in the mft bitmap attribute on the ntfs volume + * @vol. + * + * If @base_ni is NULL start the search at the default allocator position. + * + * If @base_ni is not NULL start the search at the mft record after the base + * mft record @base_ni. + * + * Return the free mft record on success and -1 on error with errno set to the + * error code. An error code of ENOSPC means that there are no free mft + * records in the currently initialized mft bitmap. + */ +static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni) +{ + s64 pass_end, ll, data_pos, pass_start, ofs, bit; + ntfs_attr *mftbmp_na; + u8 *buf, *byte; + unsigned int size; + u8 pass, b; + int ret = -1; + + ntfs_log_enter("Entering\n"); + + mftbmp_na = vol->mftbmp_na; + /* + * Set the end of the pass making sure we do not overflow the mft + * bitmap. + */ + size = PAGE_SIZE; + pass_end = vol->mft_na->allocated_size >> vol->mft_record_size_bits; + ll = mftbmp_na->initialized_size << 3; + if (pass_end > ll) + pass_end = ll; + pass = 1; + if (!base_ni) + data_pos = vol->mft_data_pos; + else + data_pos = base_ni->mft_no + 1; + if (data_pos < RESERVED_MFT_RECORDS) + data_pos = RESERVED_MFT_RECORDS; + if (data_pos >= pass_end) { + data_pos = RESERVED_MFT_RECORDS; + pass = 2; + /* This happens on a freshly formatted volume. */ + if (data_pos >= pass_end) { + errno = ENOSPC; + goto leave; + } + } + if (ntfs_is_mft(base_ni)) { + data_pos = 0; + pass = 2; + } + pass_start = data_pos; + buf = ntfs_malloc(PAGE_SIZE); + if (!buf) + goto leave; + + ntfs_log_debug("Starting bitmap search: pass %u, pass_start 0x%llx, " + "pass_end 0x%llx, data_pos 0x%llx.\n", pass, + (long long)pass_start, (long long)pass_end, + (long long)data_pos); +#ifdef DEBUG + byte = NULL; + b = 0; +#endif + /* Loop until a free mft record is found. */ + for (; pass <= 2; size = PAGE_SIZE) { + /* Cap size to pass_end. */ + ofs = data_pos >> 3; + ll = ((pass_end + 7) >> 3) - ofs; + if (size > ll) + size = ll; + ll = ntfs_attr_pread(mftbmp_na, ofs, size, buf); + if (ll < 0) { + ntfs_log_perror("Failed to read $MFT bitmap"); + free(buf); + goto leave; + } + ntfs_log_debug("Read 0x%llx bytes.\n", (long long)ll); + /* If we read at least one byte, search @buf for a zero bit. */ + if (ll) { + size = ll << 3; + bit = data_pos & 7; + data_pos &= ~7ull; + ntfs_log_debug("Before inner for loop: size 0x%x, " + "data_pos 0x%llx, bit 0x%llx, " + "*byte 0x%hhx, b %u.\n", size, + (long long)data_pos, (long long)bit, + byte ? *byte : -1, b); + for (; bit < size && data_pos + bit < pass_end; + bit &= ~7ull, bit += 8) { + /* + * If we're extending $MFT and running out of the first + * mft record (base record) then give up searching since + * no guarantee that the found record will be accessible. + */ + if (ntfs_is_mft(base_ni) && bit > 400) + goto out; + + byte = buf + (bit >> 3); + if (*byte == 0xff) + continue; + + /* Note: ffz() result must be zero based. */ + b = ntfs_ffz((unsigned long)*byte); + if (b < 8 && b >= (bit & 7)) { + free(buf); + ret = data_pos + (bit & ~7ull) + b; + goto leave; + } + } + ntfs_log_debug("After inner for loop: size 0x%x, " + "data_pos 0x%llx, bit 0x%llx, " + "*byte 0x%hhx, b %u.\n", size, + (long long)data_pos, (long long)bit, + byte ? *byte : -1, b); + data_pos += size; + /* + * If the end of the pass has not been reached yet, + * continue searching the mft bitmap for a zero bit. + */ + if (data_pos < pass_end) + continue; + } + /* Do the next pass. */ + pass++; + if (pass == 2) { + /* + * Starting the second pass, in which we scan the first + * part of the zone which we omitted earlier. + */ + pass_end = pass_start; + data_pos = pass_start = RESERVED_MFT_RECORDS; + ntfs_log_debug("pass %i, pass_start 0x%llx, pass_end " + "0x%llx.\n", pass, (long long)pass_start, + (long long)pass_end); + if (data_pos >= pass_end) + break; + } + } + /* No free mft records in currently initialized mft bitmap. */ +out: + free(buf); + errno = ENOSPC; +leave: + ntfs_log_leave("\n"); + return ret; +} + +static int ntfs_mft_attr_extend(ntfs_attr *na) +{ + int ret = STATUS_ERROR; + ntfs_log_enter("Entering\n"); + + if (!NInoAttrList(na->ni)) { + if (ntfs_inode_add_attrlist(na->ni)) { + ntfs_log_perror("%s: Can not add attrlist #3", __FUNCTION__); + goto out; + } + /* We can't sync the $MFT inode since its runlist is bogus. */ + ret = STATUS_KEEP_SEARCHING; + goto out; + } + + if (ntfs_attr_update_mapping_pairs(na, 0)) { + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + goto out; + } + + ret = STATUS_OK; +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_mft_bitmap_extend_allocation_i - see ntfs_mft_bitmap_extend_allocation + */ +static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol) +{ + LCN lcn; + s64 ll = 0; /* silence compiler warning */ + ntfs_attr *mftbmp_na; + runlist_element *rl, *rl2 = NULL; /* silence compiler warning */ + ntfs_attr_search_ctx *ctx; + MFT_RECORD *m = NULL; /* silence compiler warning */ + ATTR_RECORD *a = NULL; /* silence compiler warning */ + int err, mp_size; + int ret = STATUS_ERROR; + u32 old_alen = 0; /* silence compiler warning */ + BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; + + mftbmp_na = vol->mftbmp_na; + /* + * Determine the last lcn of the mft bitmap. The allocated size of the + * mft bitmap cannot be zero so we are ok to do this. + */ + rl = ntfs_attr_find_vcn(mftbmp_na, (mftbmp_na->allocated_size - 1) >> + vol->cluster_size_bits); + if (!rl || !rl->length || rl->lcn < 0) { + ntfs_log_error("Failed to determine last allocated " + "cluster of mft bitmap attribute.\n"); + if (rl) + errno = EIO; + return STATUS_ERROR; + } + lcn = rl->lcn + rl->length; + + rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE); + if (!rl2) { + ntfs_log_error("Failed to allocate a cluster for " + "the mft bitmap.\n"); + return STATUS_ERROR; + } + rl = ntfs_runlists_merge(mftbmp_na->rl, rl2); + if (!rl) { + err = errno; + ntfs_log_error("Failed to merge runlists for mft " + "bitmap.\n"); + if (ntfs_cluster_free_from_rl(vol, rl2)) + ntfs_log_error("Failed to deallocate " + "cluster.%s\n", es); + free(rl2); + errno = err; + return STATUS_ERROR; + } + mftbmp_na->rl = rl; + ntfs_log_debug("Adding one run to mft bitmap.\n"); + /* Find the last run in the new runlist. */ + for (; rl[1].length; rl++) + ; + /* + * Update the attribute record as well. Note: @rl is the last + * (non-terminator) runlist element of mft bitmap. + */ + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto undo_alloc; + + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft bitmap attribute.\n"); + goto undo_alloc; + } + m = ctx->mrec; + a = ctx->attr; + ll = sle64_to_cpu(a->lowest_vcn); + rl2 = ntfs_attr_find_vcn(mftbmp_na, ll); + if (!rl2 || !rl2->length) { + ntfs_log_error("Failed to determine previous last " + "allocated cluster of mft bitmap attribute.\n"); + if (rl2) + errno = EIO; + goto undo_alloc; + } + /* Get the size for the new mapping pairs array for this extent. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX); + if (mp_size <= 0) { + ntfs_log_error("Get size for mapping pairs failed for " + "mft bitmap attribute extent.\n"); + goto undo_alloc; + } + /* Expand the attribute record if necessary. */ + old_alen = le32_to_cpu(a->length); + if (ntfs_attr_record_resize(m, a, mp_size + + le16_to_cpu(a->mapping_pairs_offset))) { + ntfs_log_info("extending $MFT bitmap\n"); + ret = ntfs_mft_attr_extend(vol->mftbmp_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } + goto undo_alloc; + } + mp_rebuilt = TRUE; + /* Generate the mapping pairs array directly into the attr record. */ + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), mp_size, rl2, ll, + NULL)) { + ntfs_log_error("Failed to build mapping pairs array for " + "mft bitmap attribute.\n"); + errno = EIO; + goto undo_alloc; + } + /* Update the highest_vcn. */ + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1); + /* + * We now have extended the mft bitmap allocated_size by one cluster. + * Reflect this in the ntfs_attr structure and the attribute record. + */ + if (a->lowest_vcn) { + /* + * We are not in the first attribute extent, switch to it, but + * first ensure the changes will make it to disk later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute " + "extent of mft bitmap attribute.\n"); + goto restore_undo_alloc; + } + a = ctx->attr; + } +ok: + mftbmp_na->allocated_size += vol->cluster_size; + a->allocated_size = cpu_to_sle64(mftbmp_na->allocated_size); + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + return STATUS_OK; + +restore_undo_alloc: + err = errno; + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft bitmap attribute.%s\n", es); + ntfs_attr_put_search_ctx(ctx); + mftbmp_na->allocated_size += vol->cluster_size; + /* + * The only thing that is now wrong is ->allocated_size of the + * base attribute extent which chkdsk should be able to fix. + */ + errno = err; + return STATUS_ERROR; + } + m = ctx->mrec; + a = ctx->attr; + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 2); + errno = err; +undo_alloc: + err = errno; + + /* Remove the last run from the runlist. */ + lcn = rl->lcn; + rl->lcn = rl[1].lcn; + rl->length = 0; + + /* FIXME: use an ntfs_cluster_free_* function */ + if (ntfs_bitmap_clear_bit(vol->lcnbmp_na, lcn)) + ntfs_log_error("Failed to free cluster.%s\n", es); + else + vol->free_clusters++; + if (mp_rebuilt) { + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), + old_alen - le16_to_cpu(a->mapping_pairs_offset), + rl2, ll, NULL)) + ntfs_log_error("Failed to restore mapping " + "pairs array.%s\n", es); + if (ntfs_attr_record_resize(m, a, old_alen)) + ntfs_log_error("Failed to restore attribute " + "record.%s\n", es); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mftbmp_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + return ret; +} + +/** + * ntfs_mft_bitmap_extend_allocation - extend mft bitmap attribute by a cluster + * @vol: volume on which to extend the mft bitmap attribute + * + * Extend the mft bitmap attribute on the ntfs volume @vol by one cluster. + * + * Note: Only changes allocated_size, i.e. does not touch initialized_size or + * data_size. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) +{ + int ret; + + ntfs_log_enter("Entering\n"); + ret = ntfs_mft_bitmap_extend_allocation_i(vol); + ntfs_log_leave("\n"); + return ret; +} +/** + * ntfs_mft_bitmap_extend_initialized - extend mft bitmap initialized data + * @vol: volume on which to extend the mft bitmap attribute + * + * Extend the initialized portion of the mft bitmap attribute on the ntfs + * volume @vol by 8 bytes. + * + * Note: Only changes initialized_size and data_size, i.e. requires that + * allocated_size is big enough to fit the new initialized_size. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol) +{ + s64 old_data_size, old_initialized_size, ll; + ntfs_attr *mftbmp_na; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + int err; + int ret = -1; + + ntfs_log_enter("Entering\n"); + + mftbmp_na = vol->mftbmp_na; + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto out; + + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft bitmap attribute.\n"); + err = errno; + goto put_err_out; + } + a = ctx->attr; + old_data_size = mftbmp_na->data_size; + old_initialized_size = mftbmp_na->initialized_size; + mftbmp_na->initialized_size += 8; + a->initialized_size = cpu_to_sle64(mftbmp_na->initialized_size); + if (mftbmp_na->initialized_size > mftbmp_na->data_size) { + mftbmp_na->data_size = mftbmp_na->initialized_size; + a->data_size = cpu_to_sle64(mftbmp_na->data_size); + } + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + /* Initialize the mft bitmap attribute value with zeroes. */ + ll = 0; + ll = ntfs_attr_pwrite(mftbmp_na, old_initialized_size, 8, &ll); + if (ll == 8) { + ntfs_log_debug("Wrote eight initialized bytes to mft bitmap.\n"); + vol->free_mft_records += (8 * 8); + ret = 0; + goto out; + } + ntfs_log_error("Failed to write to mft bitmap.\n"); + err = errno; + if (ll >= 0) + err = EIO; + /* Try to recover from the error. */ + ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL); + if (!ctx) + goto err_out; + + if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name, + mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft bitmap attribute.%s\n", es); +put_err_out: + ntfs_attr_put_search_ctx(ctx); + goto err_out; + } + a = ctx->attr; + mftbmp_na->initialized_size = old_initialized_size; + a->initialized_size = cpu_to_sle64(old_initialized_size); + if (mftbmp_na->data_size != old_data_size) { + mftbmp_na->data_size = old_data_size; + a->data_size = cpu_to_sle64(old_data_size); + } + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("Restored status of mftbmp: allocated_size 0x%llx, " + "data_size 0x%llx, initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); +err_out: + errno = err; +out: + ntfs_log_leave("\n"); + return ret; +} + +/** + * ntfs_mft_data_extend_allocation - extend mft data attribute + * @vol: volume on which to extend the mft data attribute + * + * Extend the mft data attribute on the ntfs volume @vol by 16 mft records + * worth of clusters or if not enough space for this by one mft record worth + * of clusters. + * + * Note: Only changes allocated_size, i.e. does not touch initialized_size or + * data_size. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_data_extend_allocation(ntfs_volume *vol) +{ + LCN lcn; + VCN old_last_vcn; + s64 min_nr, nr, ll = 0; /* silence compiler warning */ + ntfs_attr *mft_na; + runlist_element *rl, *rl2; + ntfs_attr_search_ctx *ctx; + MFT_RECORD *m = NULL; /* silence compiler warning */ + ATTR_RECORD *a = NULL; /* silence compiler warning */ + int err, mp_size; + int ret = STATUS_ERROR; + u32 old_alen = 0; /* silence compiler warning */ + BOOL mp_rebuilt = FALSE; + BOOL update_mp = FALSE; + + ntfs_log_enter("Extending mft data allocation.\n"); + + mft_na = vol->mft_na; + /* + * Determine the preferred allocation location, i.e. the last lcn of + * the mft data attribute. The allocated size of the mft data + * attribute cannot be zero so we are ok to do this. + */ + rl = ntfs_attr_find_vcn(mft_na, + (mft_na->allocated_size - 1) >> vol->cluster_size_bits); + + if (!rl || !rl->length || rl->lcn < 0) { + ntfs_log_error("Failed to determine last allocated " + "cluster of mft data attribute.\n"); + if (rl) + errno = EIO; + goto out; + } + + lcn = rl->lcn + rl->length; + ntfs_log_debug("Last lcn of mft data attribute is 0x%llx.\n", (long long)lcn); + /* Minimum allocation is one mft record worth of clusters. */ + min_nr = vol->mft_record_size >> vol->cluster_size_bits; + if (!min_nr) + min_nr = 1; + /* Want to allocate 16 mft records worth of clusters. */ + nr = vol->mft_record_size << 4 >> vol->cluster_size_bits; + if (!nr) + nr = min_nr; + + old_last_vcn = rl[1].vcn; + do { + rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE); + if (rl2) + break; + if (errno != ENOSPC || nr == min_nr) { + ntfs_log_perror("Failed to allocate (%lld) clusters " + "for $MFT", (long long)nr); + goto out; + } + /* + * There is not enough space to do the allocation, but there + * might be enough space to do a minimal allocation so try that + * before failing. + */ + nr = min_nr; + ntfs_log_debug("Retrying mft data allocation with minimal cluster " + "count %lli.\n", (long long)nr); + } while (1); + + ntfs_log_debug("Allocated %lld clusters.\n", (long long)nr); + + rl = ntfs_runlists_merge(mft_na->rl, rl2); + if (!rl) { + err = errno; + ntfs_log_error("Failed to merge runlists for mft data " + "attribute.\n"); + if (ntfs_cluster_free_from_rl(vol, rl2)) + ntfs_log_error("Failed to deallocate clusters " + "from the mft data attribute.%s\n", es); + free(rl2); + errno = err; + goto out; + } + mft_na->rl = rl; + + /* Find the last run in the new runlist. */ + for (; rl[1].length; rl++) + ; + /* Update the attribute record as well. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_alloc; + + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft data attribute.\n"); + goto undo_alloc; + } + m = ctx->mrec; + a = ctx->attr; + ll = sle64_to_cpu(a->lowest_vcn); + rl2 = ntfs_attr_find_vcn(mft_na, ll); + if (!rl2 || !rl2->length) { + ntfs_log_error("Failed to determine previous last " + "allocated cluster of mft data attribute.\n"); + if (rl2) + errno = EIO; + goto undo_alloc; + } + /* Get the size for the new mapping pairs array for this extent. */ + mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX); + if (mp_size <= 0) { + ntfs_log_error("Get size for mapping pairs failed for " + "mft data attribute extent.\n"); + goto undo_alloc; + } + /* Expand the attribute record if necessary. */ + old_alen = le32_to_cpu(a->length); + if (ntfs_attr_record_resize(m, a, + mp_size + le16_to_cpu(a->mapping_pairs_offset))) { + ret = ntfs_mft_attr_extend(vol->mft_na); + if (ret == STATUS_OK) + goto ok; + if (ret == STATUS_ERROR) { + ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__); + update_mp = TRUE; + } + goto undo_alloc; + } + mp_rebuilt = TRUE; + /* + * Generate the mapping pairs array directly into the attribute record. + */ + if (ntfs_mapping_pairs_build(vol, + (u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp_size, + rl2, ll, NULL)) { + ntfs_log_error("Failed to build mapping pairs array of " + "mft data attribute.\n"); + errno = EIO; + goto undo_alloc; + } + /* Update the highest_vcn. */ + a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1); + /* + * We now have extended the mft data allocated_size by nr clusters. + * Reflect this in the ntfs_attr structure and the attribute record. + * @rl is the last (non-terminator) runlist element of mft data + * attribute. + */ + if (a->lowest_vcn) { + /* + * We are not in the first attribute extent, switch to it, but + * first ensure the changes will make it to disk later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mft_na->type, mft_na->name, + mft_na->name_len, 0, 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute " + "extent of mft data attribute.\n"); + goto restore_undo_alloc; + } + a = ctx->attr; + } +ok: + mft_na->allocated_size += nr << vol->cluster_size_bits; + a->allocated_size = cpu_to_sle64(mft_na->allocated_size); + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ret = STATUS_OK; +out: + ntfs_log_leave("\n"); + return ret; + +restore_undo_alloc: + err = errno; + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + rl[1].vcn, NULL, 0, ctx)) { + ntfs_log_error("Failed to find last attribute extent of " + "mft data attribute.%s\n", es); + ntfs_attr_put_search_ctx(ctx); + mft_na->allocated_size += nr << vol->cluster_size_bits; + /* + * The only thing that is now wrong is ->allocated_size of the + * base attribute extent which chkdsk should be able to fix. + */ + errno = err; + ret = STATUS_ERROR; + goto out; + } + m = ctx->mrec; + a = ctx->attr; + a->highest_vcn = cpu_to_sle64(old_last_vcn - 1); + errno = err; +undo_alloc: + err = errno; + if (ntfs_cluster_free(vol, mft_na, old_last_vcn, -1) < 0) + ntfs_log_error("Failed to free clusters from mft data " + "attribute.%s\n", es); + if (ntfs_rl_truncate(&mft_na->rl, old_last_vcn)) + ntfs_log_error("Failed to truncate mft data attribute " + "runlist.%s\n", es); + if (mp_rebuilt) { + if (ntfs_mapping_pairs_build(vol, (u8*)a + + le16_to_cpu(a->mapping_pairs_offset), + old_alen - le16_to_cpu(a->mapping_pairs_offset), + rl2, ll, NULL)) + ntfs_log_error("Failed to restore mapping pairs " + "array.%s\n", es); + if (ntfs_attr_record_resize(m, a, old_alen)) + ntfs_log_error("Failed to restore attribute " + "record.%s\n", es); + ntfs_inode_mark_dirty(ctx->ntfs_ino); + } + if (update_mp) { + if (ntfs_attr_update_mapping_pairs(vol->mft_na, 0)) + ntfs_log_perror("%s: MP update failed", __FUNCTION__); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + goto out; +} + + +static int ntfs_mft_record_init(ntfs_volume *vol, s64 size) +{ + int ret = -1; + ntfs_attr *mft_na; + s64 old_data_initialized, old_data_size; + ntfs_attr_search_ctx *ctx; + + ntfs_log_enter("Entering\n"); + + /* NOTE: Caller must sanity check vol, vol->mft_na and vol->mftbmp_na */ + + mft_na = vol->mft_na; + + /* + * The mft record is outside the initialized data. Extend the mft data + * attribute until it covers the allocated record. The loop is only + * actually traversed more than once when a freshly formatted volume + * is first written to so it optimizes away nicely in the common case. + */ + ntfs_log_debug("Status of mft data before extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + while (size > mft_na->allocated_size) { + if (ntfs_mft_data_extend_allocation(vol) == STATUS_ERROR) + goto out; + ntfs_log_debug("Status of mft data after allocation extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + } + + old_data_initialized = mft_na->initialized_size; + old_data_size = mft_na->data_size; + + /* + * Extend mft data initialized size (and data size of course) to reach + * the allocated mft record, formatting the mft records along the way. + * Note: We only modify the ntfs_attr structure as that is all that is + * needed by ntfs_mft_record_format(). We will update the attribute + * record itself in one fell swoop later on. + */ + while (size > mft_na->initialized_size) { + s64 ll2 = mft_na->initialized_size >> vol->mft_record_size_bits; + mft_na->initialized_size += vol->mft_record_size; + if (mft_na->initialized_size > mft_na->data_size) + mft_na->data_size = mft_na->initialized_size; + ntfs_log_debug("Initializing mft record 0x%llx.\n", (long long)ll2); + if (ntfs_mft_record_format(vol, ll2) < 0) { + ntfs_log_perror("Failed to format mft record"); + goto undo_data_init; + } + } + + /* Update the mft data attribute record to reflect the new sizes. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_data_init; + + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft data attribute.\n"); + ntfs_attr_put_search_ctx(ctx); + goto undo_data_init; + } + ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); + ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + ctx->attr->allocated_size = cpu_to_sle64(mft_na->allocated_size); + + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("Status of mft data after mft record initialization: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + + /* Sanity checks. */ + if (mft_na->data_size > mft_na->allocated_size || + mft_na->initialized_size > mft_na->data_size) + NTFS_BUG("mft_na sanity checks failed"); + + /* Sync MFT to minimize data loss if there won't be clean unmount. */ + if (ntfs_inode_sync(mft_na->ni)) + goto undo_data_init; + + ret = 0; +out: + ntfs_log_leave("\n"); + return ret; + +undo_data_init: + mft_na->initialized_size = old_data_initialized; + mft_na->data_size = old_data_size; + goto out; +} + +static int ntfs_mft_rec_init(ntfs_volume *vol, s64 size) +{ + int ret = -1; + ntfs_attr *mft_na; + s64 old_data_initialized, old_data_size; + ntfs_attr_search_ctx *ctx; + + ntfs_log_enter("Entering\n"); + + mft_na = vol->mft_na; + + if (size > mft_na->allocated_size || size > mft_na->initialized_size) { + errno = EIO; + ntfs_log_perror("%s: unexpected $MFT sizes, see below", __FUNCTION__); + ntfs_log_error("$MFT: size=%lld allocated_size=%lld " + "data_size=%lld initialized_size=%lld\n", + (long long)size, + (long long)mft_na->allocated_size, + (long long)mft_na->data_size, + (long long)mft_na->initialized_size); + goto out; + } + + old_data_initialized = mft_na->initialized_size; + old_data_size = mft_na->data_size; + + /* Update the mft data attribute record to reflect the new sizes. */ + ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL); + if (!ctx) + goto undo_data_init; + + if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0, + 0, NULL, 0, ctx)) { + ntfs_log_error("Failed to find first attribute extent of " + "mft data attribute.\n"); + ntfs_attr_put_search_ctx(ctx); + goto undo_data_init; + } + ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size); + ctx->attr->data_size = cpu_to_sle64(mft_na->data_size); + + /* CHECKME: ctx->attr->allocation_size is already ok? */ + + /* Ensure the changes make it to disk. */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + ntfs_attr_put_search_ctx(ctx); + + /* Sanity checks. */ + if (mft_na->data_size > mft_na->allocated_size || + mft_na->initialized_size > mft_na->data_size) + NTFS_BUG("mft_na sanity checks failed"); +out: + ntfs_log_leave("\n"); + return ret; + +undo_data_init: + mft_na->initialized_size = old_data_initialized; + mft_na->data_size = old_data_size; + goto out; +} + +ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol, BOOL mft_data) +{ + s64 ll, bit; + ntfs_attr *mft_na, *mftbmp_na; + MFT_RECORD *m; + ntfs_inode *ni = NULL; + ntfs_inode *base_ni; + int err; + le16 seq_no, usn; + BOOL forced_mft_data; + + ntfs_log_enter("Entering\n"); + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; + + base_ni = mft_na->ni; + + /* + * The first extent containing $MFT:$AT_DATA is better located + * in record 15 to make sure it can be read at mount time. + * The record 15 is prereserved as a base inode with no + * extents and no name, and it is marked in use. + */ + forced_mft_data = FALSE; + if (mft_data) { + ntfs_inode *ext_ni = ntfs_inode_open(vol, FILE_mft_data); + /* + * If record 15 cannot be opened, it is probably in + * use as an extent. Apply standard procedure for + * further extents. + */ + if (ext_ni) { + /* + * Make sure record 15 is a base extent and has + * no extents. + * Also make sure it has no name : a base inode with + * no extents and no name cannot be in use. + * Otherwise apply standard procedure. + */ + if (!ext_ni->mrec->base_mft_record + && !ext_ni->nr_extents) + forced_mft_data = TRUE; + ntfs_inode_close(ext_ni); + } + } + if (forced_mft_data) + bit = FILE_mft_data; + else + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + if (bit >= 0) + goto found_free_rec; + + if (errno != ENOSPC) + goto out; + + errno = ENOSPC; + /* strerror() is intentionally used below, we want to log this error. */ + ntfs_log_error("No free mft record for $MFT: %s\n", strerror(errno)); + goto err_out; + +found_free_rec: + if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { + ntfs_log_error("Failed to allocate bit in mft bitmap #2\n"); + goto err_out; + } + + ll = (bit + 1) << vol->mft_record_size_bits; + if (ll > mft_na->initialized_size) + if (ntfs_mft_rec_init(vol, ll) < 0) + goto undo_mftbmp_alloc; + /* + * We now have allocated and initialized the mft record. Need to read + * it from disk and re-format it, preserving the sequence number if it + * is not zero as well as the update sequence number if it is not zero + * or -1 (0xffff). + */ + m = ntfs_malloc(vol->mft_record_size); + if (!m) + goto undo_mftbmp_alloc; + + if (ntfs_mft_record_read(vol, bit, m)) { + free(m); + goto undo_mftbmp_alloc; + } + /* Sanity check that the mft record is really not in use. */ + if (!forced_mft_data + && (ntfs_is_file_record(m->magic) + && (m->flags & MFT_RECORD_IN_USE))) { + ntfs_log_error("Inode %lld is used but it wasn't marked in " + "$MFT bitmap. Fixed.\n", (long long)bit); + free(m); + goto undo_mftbmp_alloc; + } + + seq_no = m->sequence_number; + usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)); + if (ntfs_mft_record_layout(vol, bit, m)) { + ntfs_log_error("Failed to re-format mft record.\n"); + free(m); + goto undo_mftbmp_alloc; + } + if (seq_no) + m->sequence_number = seq_no; + seq_no = usn; + if (seq_no && seq_no != const_cpu_to_le16(0xffff)) + *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; + /* Set the mft record itself in use. */ + m->flags |= MFT_RECORD_IN_USE; + /* Now need to open an ntfs inode for the mft record. */ + ni = ntfs_inode_allocate(vol); + if (!ni) { + ntfs_log_error("Failed to allocate buffer for inode.\n"); + free(m); + goto undo_mftbmp_alloc; + } + ni->mft_no = bit; + ni->mrec = m; + /* + * If we are allocating an extent mft record, make the opened inode an + * extent inode and attach it to the base inode. Also, set the base + * mft record reference in the extent inode. + */ + ni->nr_extents = -1; + ni->base_ni = base_ni; + m->base_mft_record = MK_LE_MREF(base_ni->mft_no, + le16_to_cpu(base_ni->mrec->sequence_number)); + /* + * Attach the extent inode to the base inode, reallocating + * memory if needed. + */ + if (!(base_ni->nr_extents & 3)) { + ntfs_inode **extent_nis; + int i; + + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + extent_nis = ntfs_malloc(i); + if (!extent_nis) { + free(m); + free(ni); + goto undo_mftbmp_alloc; + } + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; + + /* Make sure the allocated inode is written out to disk later. */ + ntfs_inode_mark_dirty(ni); + /* Initialize time, allocated and data size in ntfs_inode struct. */ + ni->data_size = ni->allocated_size = 0; + ni->flags = 0; + ni->creation_time = ni->last_data_change_time = + ni->last_mft_change_time = + ni->last_access_time = ntfs_current_time(); + /* Update the default mft allocation position if it was used. */ + if (!base_ni) + vol->mft_data_pos = bit + 1; + /* Return the opened, allocated inode of the allocated mft record. */ + ntfs_log_error("allocated %sinode %lld\n", + base_ni ? "extent " : "", (long long)bit); +out: + ntfs_log_leave("\n"); + return ni; + +undo_mftbmp_alloc: + err = errno; + if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) + ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); + errno = err; +err_out: + if (!errno) + errno = EIO; + ni = NULL; + goto out; +} + +/** + * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume + * @vol: volume on which to allocate the mft record + * @base_ni: open base inode if allocating an extent mft record or NULL + * + * Allocate an mft record in $MFT/$DATA of an open ntfs volume @vol. + * + * If @base_ni is NULL make the mft record a base mft record and allocate it at + * the default allocator position. + * + * If @base_ni is not NULL make the allocated mft record an extent record, + * allocate it starting at the mft record after the base mft record and attach + * the allocated and opened ntfs inode to the base inode @base_ni. + * + * On success return the now opened ntfs (extent) inode of the mft record. + * + * On error return NULL with errno set to the error code. + * + * To find a free mft record, we scan the mft bitmap for a zero bit. To + * optimize this we start scanning at the place specified by @base_ni or if + * @base_ni is NULL we start where we last stopped and we perform wrap around + * when we reach the end. Note, we do not try to allocate mft records below + * number 24 because numbers 0 to 15 are the defined system files anyway and 16 + * to 24 are used for storing extension mft records or used by chkdsk to store + * its log. However the record number 15 is dedicated to the first extent to + * the $DATA attribute of $MFT. This is required to avoid the possibility + * of creating a run list with a circular dependence which once written to disk + * can never be read in again. Windows will only use records 16 to 24 for + * normal files if the volume is completely out of space. We never use them + * which means that when the volume is really out of space we cannot create any + * more files while Windows can still create up to 8 small files. We can start + * doing this at some later time, it does not matter much for now. + * + * When scanning the mft bitmap, we only search up to the last allocated mft + * record. If there are no free records left in the range 24 to number of + * allocated mft records, then we extend the $MFT/$DATA attribute in order to + * create free mft records. We extend the allocated size of $MFT/$DATA by 16 + * records at a time or one cluster, if cluster size is above 16kiB. If there + * is not sufficient space to do this, we try to extend by a single mft record + * or one cluster, if cluster size is above the mft record size, but we only do + * this if there is enough free space, which we know from the values returned + * by the failed cluster allocation function when we tried to do the first + * allocation. + * + * No matter how many mft records we allocate, we initialize only the first + * allocated mft record, incrementing mft data size and initialized size + * accordingly, open an ntfs_inode for it and return it to the caller, unless + * there are less than 24 mft records, in which case we allocate and initialize + * mft records until we reach record 24 which we consider as the first free mft + * record for use by normal files. + * + * If during any stage we overflow the initialized data in the mft bitmap, we + * extend the initialized size (and data size) by 8 bytes, allocating another + * cluster if required. The bitmap data size has to be at least equal to the + * number of mft records in the mft, but it can be bigger, in which case the + * superfluous bits are padded with zeroes. + * + * Thus, when we return successfully (return value non-zero), we will have: + * - initialized / extended the mft bitmap if necessary, + * - initialized / extended the mft data if necessary, + * - set the bit corresponding to the mft record being allocated in the + * mft bitmap, + * - open an ntfs_inode for the allocated mft record, and we will + * - return the ntfs_inode. + * + * On error (return value zero), nothing will have changed. If we had changed + * anything before the error occurred, we will have reverted back to the + * starting state before returning to the caller. Thus, except for bugs, we + * should always leave the volume in a consistent state when returning from + * this function. + * + * Note, this function cannot make use of most of the normal functions, like + * for example for attribute resizing, etc, because when the run list overflows + * the base mft record and an attribute list is used, it is very important that + * the extension mft records used to store the $DATA attribute of $MFT can be + * reached without having to read the information contained inside them, as + * this would make it impossible to find them in the first place after the + * volume is dismounted. $MFT/$BITMAP probably does not need to follow this + * rule because the bitmap is not essential for finding the mft records, but on + * the other hand, handling the bitmap in this special way would make life + * easier because otherwise there might be circular invocations of functions + * when reading the bitmap but if we are careful, we should be able to avoid + * all problems. + */ +//ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) +ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) +{ + s64 ll, bit; + ntfs_attr *mft_na, *mftbmp_na; + MFT_RECORD *m; + ntfs_inode *ni = NULL; + int err; + le16 seq_no, usn; + + if (base_ni) + ntfs_log_enter("Entering (allocating an extent mft record for " + "base mft record %lld).\n", + (long long)base_ni->mft_no); + else + ntfs_log_enter("Entering (allocating a base mft record)\n"); + if (!vol || !vol->mft_na || !vol->mftbmp_na) { + errno = EINVAL; + goto out; + } + + if (ntfs_is_mft(base_ni)) { + ni = ntfs_mft_rec_alloc(vol, FALSE); + goto out; + } + + mft_na = vol->mft_na; + mftbmp_na = vol->mftbmp_na; +retry: + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + if (bit >= 0) { + ntfs_log_debug("found free record (#1) at %lld\n", + (long long)bit); + goto found_free_rec; + } + if (errno != ENOSPC) + goto out; + /* + * No free mft records left. If the mft bitmap already covers more + * than the currently used mft records, the next records are all free, + * so we can simply allocate the first unused mft record. + * Note: We also have to make sure that the mft bitmap at least covers + * the first 24 mft records as they are special and whilst they may not + * be in use, we do not allocate from them. + */ + ll = mft_na->initialized_size >> vol->mft_record_size_bits; + if (mftbmp_na->initialized_size << 3 > ll && + mftbmp_na->initialized_size > RESERVED_MFT_RECORDS / 8) { + bit = ll; + if (bit < RESERVED_MFT_RECORDS) + bit = RESERVED_MFT_RECORDS; + ntfs_log_debug("found free record (#2) at %lld\n", + (long long)bit); + goto found_free_rec; + } + /* + * The mft bitmap needs to be expanded until it covers the first unused + * mft record that we can allocate. + * Note: The smallest mft record we allocate is mft record 24. + */ + ntfs_log_debug("Status of mftbmp before extension: allocated_size 0x%llx, " + "data_size 0x%llx, initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + if (mftbmp_na->initialized_size + 8 > mftbmp_na->allocated_size) { + + int ret = ntfs_mft_bitmap_extend_allocation(vol); + + if (ret == STATUS_ERROR) + goto err_out; + if (ret == STATUS_KEEP_SEARCHING) { + ret = ntfs_mft_bitmap_extend_allocation(vol); + if (ret != STATUS_OK) + goto err_out; + } + + ntfs_log_debug("Status of mftbmp after allocation extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + } + /* + * We now have sufficient allocated space, extend the initialized_size + * as well as the data_size if necessary and fill the new space with + * zeroes. + */ + bit = mftbmp_na->initialized_size << 3; + if (ntfs_mft_bitmap_extend_initialized(vol)) + goto err_out; + ntfs_log_debug("Status of mftbmp after initialized extension: " + "allocated_size 0x%llx, data_size 0x%llx, " + "initialized_size 0x%llx.\n", + (long long)mftbmp_na->allocated_size, + (long long)mftbmp_na->data_size, + (long long)mftbmp_na->initialized_size); + ntfs_log_debug("found free record (#3) at %lld\n", (long long)bit); +found_free_rec: + /* @bit is the found free mft record, allocate it in the mft bitmap. */ + if (ntfs_bitmap_set_bit(mftbmp_na, bit)) { + ntfs_log_error("Failed to allocate bit in mft bitmap.\n"); + goto err_out; + } + + /* The mft bitmap is now uptodate. Deal with mft data attribute now. */ + ll = (bit + 1) << vol->mft_record_size_bits; + if (ll > mft_na->initialized_size) + if (ntfs_mft_record_init(vol, ll) < 0) + goto undo_mftbmp_alloc; + + /* + * We now have allocated and initialized the mft record. Need to read + * it from disk and re-format it, preserving the sequence number if it + * is not zero as well as the update sequence number if it is not zero + * or -1 (0xffff). + */ + m = ntfs_malloc(vol->mft_record_size); + if (!m) + goto undo_mftbmp_alloc; + + if (ntfs_mft_record_read(vol, bit, m)) { + free(m); + goto undo_mftbmp_alloc; + } + /* Sanity check that the mft record is really not in use. */ + if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { + ntfs_log_error("Inode %lld is used but it wasn't marked in " + "$MFT bitmap. Fixed.\n", (long long)bit); + free(m); + goto retry; + } + seq_no = m->sequence_number; + usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)); + if (ntfs_mft_record_layout(vol, bit, m)) { + ntfs_log_error("Failed to re-format mft record.\n"); + free(m); + goto undo_mftbmp_alloc; + } + if (seq_no) + m->sequence_number = seq_no; + seq_no = usn; + if (seq_no && seq_no != const_cpu_to_le16(0xffff)) + *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn; + /* Set the mft record itself in use. */ + m->flags |= MFT_RECORD_IN_USE; + /* Now need to open an ntfs inode for the mft record. */ + ni = ntfs_inode_allocate(vol); + if (!ni) { + ntfs_log_error("Failed to allocate buffer for inode.\n"); + free(m); + goto undo_mftbmp_alloc; + } + ni->mft_no = bit; + ni->mrec = m; + /* + * If we are allocating an extent mft record, make the opened inode an + * extent inode and attach it to the base inode. Also, set the base + * mft record reference in the extent inode. + */ + if (base_ni) { + ni->nr_extents = -1; + ni->base_ni = base_ni; + m->base_mft_record = MK_LE_MREF(base_ni->mft_no, + le16_to_cpu(base_ni->mrec->sequence_number)); + /* + * Attach the extent inode to the base inode, reallocating + * memory if needed. + */ + if (!(base_ni->nr_extents & 3)) { + ntfs_inode **extent_nis; + int i; + + i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *); + extent_nis = ntfs_malloc(i); + if (!extent_nis) { + free(m); + free(ni); + goto undo_mftbmp_alloc; + } + if (base_ni->nr_extents) { + memcpy(extent_nis, base_ni->extent_nis, + i - 4 * sizeof(ntfs_inode *)); + free(base_ni->extent_nis); + } + base_ni->extent_nis = extent_nis; + } + base_ni->extent_nis[base_ni->nr_extents++] = ni; + } + /* Make sure the allocated inode is written out to disk later. */ + ntfs_inode_mark_dirty(ni); + /* Initialize time, allocated and data size in ntfs_inode struct. */ + ni->data_size = ni->allocated_size = 0; + ni->flags = 0; + ni->creation_time = ni->last_data_change_time = + ni->last_mft_change_time = + ni->last_access_time = ntfs_current_time(); + /* Update the default mft allocation position if it was used. */ + if (!base_ni) + vol->mft_data_pos = bit + 1; + /* Return the opened, allocated inode of the allocated mft record. */ + ntfs_log_debug("allocated %sinode 0x%llx.\n", + base_ni ? "extent " : "", (long long)bit); + vol->free_mft_records--; +out: + ntfs_log_leave("\n"); + return ni; + +undo_mftbmp_alloc: + err = errno; + if (ntfs_bitmap_clear_bit(mftbmp_na, bit)) + ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es); + errno = err; +err_out: + if (!errno) + errno = EIO; + ni = NULL; + goto out; +} + +/** + * ntfs_mft_record_free - free an mft record on an ntfs volume + * @vol: volume on which to free the mft record + * @ni: open ntfs inode of the mft record to free + * + * Free the mft record of the open inode @ni on the mounted ntfs volume @vol. + * Note that this function calls ntfs_inode_close() internally and hence you + * cannot use the pointer @ni any more after this function returns success. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni) +{ + u64 mft_no; + int err; + u16 seq_no; + le16 old_seq_no; + + ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); + + if (!vol || !vol->mftbmp_na || !ni) { + errno = EINVAL; + return -1; + } + + /* Cache the mft reference for later. */ + mft_no = ni->mft_no; + + /* Mark the mft record as not in use. */ + ni->mrec->flags &= ~MFT_RECORD_IN_USE; + + /* Increment the sequence number, skipping zero, if it is not zero. */ + old_seq_no = ni->mrec->sequence_number; + seq_no = le16_to_cpu(old_seq_no); + if (seq_no == 0xffff) + seq_no = 1; + else if (seq_no) + seq_no++; + ni->mrec->sequence_number = cpu_to_le16(seq_no); + + /* Set the inode dirty and write it out. */ + ntfs_inode_mark_dirty(ni); + if (ntfs_inode_sync(ni)) { + err = errno; + goto sync_rollback; + } + + /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */ + if (ntfs_bitmap_clear_bit(vol->mftbmp_na, mft_no)) { + err = errno; + // FIXME: If ntfs_bitmap_clear_run() guarantees rollback on + // error, this could be changed to goto sync_rollback; + goto bitmap_rollback; + } + + /* Throw away the now freed inode. */ +#if CACHE_NIDATA_SIZE + if (!ntfs_inode_real_close(ni)) { +#else + if (!ntfs_inode_close(ni)) { +#endif + vol->free_mft_records++; + return 0; + } + err = errno; + + /* Rollback what we did... */ +bitmap_rollback: + if (ntfs_bitmap_set_bit(vol->mftbmp_na, mft_no)) + ntfs_log_debug("Eeek! Rollback failed in ntfs_mft_record_free(). " + "Leaving inconsistent metadata!\n"); +sync_rollback: + ni->mrec->flags |= MFT_RECORD_IN_USE; + ni->mrec->sequence_number = old_seq_no; + ntfs_inode_mark_dirty(ni); + errno = err; + return -1; +} + +/** + * ntfs_mft_usn_dec - Decrement USN by one + * @mrec: pointer to an mft record + * + * On success return 0 and on error return -1 with errno set. + */ +int ntfs_mft_usn_dec(MFT_RECORD *mrec) +{ + u16 usn; + le16 *usnp; + + if (!mrec) { + errno = EINVAL; + return -1; + } + usnp = (le16*)((char*)mrec + le16_to_cpu(mrec->usa_ofs)); + usn = le16_to_cpup(usnp); + if (usn-- <= 1) + usn = 0xfffe; + *usnp = cpu_to_le16(usn); + + return 0; +} + diff --git a/libntfs-3g/misc.c b/libntfs-3g/misc.c new file mode 100755 index 0000000000000000000000000000000000000000..b2e17cbf972fdb9758bfd5dbbc4b4c948095c32b --- /dev/null +++ b/libntfs-3g/misc.c @@ -0,0 +1,61 @@ +/** + * misc.c : miscellaneous : + * - dealing with errors in memory allocation + * + * Copyright (c) 2008 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "types.h" +#include "misc.h" +#include "logging.h" + +/** + * ntfs_calloc + * + * Return a pointer to the allocated memory or NULL if the request fails. + */ +void *ntfs_calloc(size_t size) +{ + void *p; + + p = calloc(1, size); + if (!p) + ntfs_log_perror("Failed to calloc %lld bytes", (long long)size); + return p; +} + +void *ntfs_malloc(size_t size) +{ + void *p; + + p = malloc(size); + if (!p) + ntfs_log_perror("Failed to malloc %lld bytes", (long long)size); + return p; +} diff --git a/libntfs-3g/mst.c b/libntfs-3g/mst.c new file mode 100755 index 0000000000000000000000000000000000000000..b7937b7a411c04366906210b81e582f57f3056fd --- /dev/null +++ b/libntfs-3g/mst.c @@ -0,0 +1,247 @@ +/** + * mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2006-2009 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "mst.h" +#include "logging.h" + +/** + * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data + * @b: pointer to the data to deprotect + * @size: size in bytes of @b + * + * Perform the necessary post read multi sector transfer fixups and detect the + * presence of incomplete multi sector transfers. - In that case, overwrite the + * magic of the ntfs record header being processed with "BAAD" (in memory only!) + * and abort processing. + * + * Return 0 on success and -1 on error, with errno set to the error code. The + * following error codes are defined: + * EINVAL Invalid arguments or invalid NTFS record in buffer @b. + * EIO Multi sector transfer error was detected. Magic of the NTFS + * record in @b will have been set to "BAAD". + */ +int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, + BOOL warn) +{ + u16 usa_ofs, usa_count, usn; + u16 *usa_pos, *data_pos; + + ntfs_log_trace("Entering\n"); + + /* Setup the variables. */ + usa_ofs = le16_to_cpu(b->usa_ofs); + /* Decrement usa_count to get number of fixups. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + /* Size and alignment checks. */ + if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || + (u32)(usa_ofs + (usa_count * 2)) > size || + (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { + errno = EINVAL; + if (warn) { + ntfs_log_perror("%s: magic: 0x%08lx size: %ld " + " usa_ofs: %d usa_count: %u", + __FUNCTION__, + (long)le32_to_cpu(*(le32 *)b), + (long)size, (int)usa_ofs, + (unsigned int)usa_count); + } + return -1; + } + /* Position of usn in update sequence array. */ + usa_pos = (u16*)b + usa_ofs/sizeof(u16); + /* + * The update sequence number which has to be equal to each of the + * u16 values before they are fixed up. Note no need to care for + * endianness since we are comparing and moving data for on disk + * structures which means the data is consistent. - If it is + * consistency the wrong endianness it doesn't make any difference. + */ + usn = *usa_pos; + /* + * Position in protected data of first u16 that needs fixing up. + */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* + * Check for incomplete multi sector transfer(s). + */ + while (usa_count--) { + if (*data_pos != usn) { + /* + * Incomplete multi sector transfer detected! )-: + * Set the magic to "BAAD" and return failure. + * Note that magic_BAAD is already converted to le32. + */ + errno = EIO; + ntfs_log_perror("Incomplete multi-sector transfer: " + "magic: 0x%08x size: %d usa_ofs: %d usa_count:" + " %d data: %d usn: %d", *(le32 *)b, size, + usa_ofs, usa_count, *data_pos, usn); + b->magic = magic_BAAD; + return -1; + } + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + /* Re-setup the variables. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment position in usa and restore original data from + * the usa into the data buffer. + */ + *data_pos = *(++usa_pos); + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + return 0; +} + +/* + * Deprotect multi sector transfer protected data + * with a warning if an error is found. + */ + +int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) +{ + return (ntfs_mst_post_read_fixup_warn(b,size,TRUE)); +} + +/** + * ntfs_mst_pre_write_fixup - apply multi sector transfer protection + * @b: pointer to the data to protect + * @size: size in bytes of @b + * + * Perform the necessary pre write multi sector transfer fixup on the data + * pointer to by @b of @size. + * + * Return 0 if fixups applied successfully or -1 if no fixups were performed + * due to errors. In that case errno i set to the error code (EINVAL). + * + * NOTE: We consider the absence / invalidity of an update sequence array to + * mean error. This means that you have to create a valid update sequence + * array header in the ntfs record before calling this function, otherwise it + * will fail (the header needs to contain the position of the update sequence + * array together with the number of elements in the array). You also need to + * initialise the update sequence number before calling this function + * otherwise a random word will be used (whatever was in the record at that + * position at that time). + */ +int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) +{ + u16 usa_ofs, usa_count, usn; + u16 *usa_pos, *data_pos; + + ntfs_log_trace("Entering\n"); + + /* Sanity check + only fixup if it makes sense. */ + if (!b || ntfs_is_baad_record(b->magic) || + ntfs_is_hole_record(b->magic)) { + errno = EINVAL; + ntfs_log_perror("%s: bad argument", __FUNCTION__); + return -1; + } + /* Setup the variables. */ + usa_ofs = le16_to_cpu(b->usa_ofs); + /* Decrement usa_count to get number of fixups. */ + usa_count = le16_to_cpu(b->usa_count) - 1; + /* Size and alignment checks. */ + if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || + (u32)(usa_ofs + (usa_count * 2)) > size || + (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { + errno = EINVAL; + ntfs_log_perror("%s", __FUNCTION__); + return -1; + } + /* Position of usn in update sequence array. */ + usa_pos = (u16*)((u8*)b + usa_ofs); + /* + * Cyclically increment the update sequence number + * (skipping 0 and -1, i.e. 0xffff). + */ + usn = le16_to_cpup(usa_pos) + 1; + if (usn == 0xffff || !usn) + usn = 1; + usn = cpu_to_le16(usn); + *usa_pos = usn; + /* Position in data of first u16 that needs fixing up. */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment the position in the usa and save the + * original data from the data buffer into the usa. + */ + *(++usa_pos) = *data_pos; + /* Apply fixup to data. */ + *data_pos = usn; + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } + return 0; +} + +/** + * ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data + * @b: pointer to the data to deprotect + * + * Perform the necessary post write multi sector transfer fixup, not checking + * for any errors, because we assume we have just used + * ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never + * have gotten here. + */ +void ntfs_mst_post_write_fixup(NTFS_RECORD *b) +{ + u16 *usa_pos, *data_pos; + + u16 usa_ofs = le16_to_cpu(b->usa_ofs); + u16 usa_count = le16_to_cpu(b->usa_count) - 1; + + ntfs_log_trace("Entering\n"); + + /* Position of usn in update sequence array. */ + usa_pos = (u16*)b + usa_ofs/sizeof(u16); + + /* Position in protected data of first u16 that needs fixing up. */ + data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; + + /* Fixup all sectors. */ + while (usa_count--) { + /* + * Increment position in usa and restore original data from + * the usa into the data buffer. + */ + *data_pos = *(++usa_pos); + + /* Increment position in data as well. */ + data_pos += NTFS_BLOCK_SIZE/sizeof(u16); + } +} + diff --git a/libntfs-3g/object_id.c b/libntfs-3g/object_id.c new file mode 100755 index 0000000000000000000000000000000000000000..299357e56241408b6882b599c6b334382d4a487e --- /dev/null +++ b/libntfs-3g/object_id.c @@ -0,0 +1,640 @@ +/** + * object_id.c - Processing of object ids + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2009 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include <sys/sysmacros.h> +#endif + +#include "compat.h" +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "lcnalloc.h" +#include "object_id.h" +#include "logging.h" +#include "misc.h" + +/* + * Endianness considerations + * + * According to RFC 4122, GUIDs should be printed with the most + * significant byte first, and the six fields be compared individually + * for ordering. RFC 4122 does not define the internal representation. + * + * Here we always copy disk images with no endianness change, + * and, for indexing, GUIDs are compared as if they were a sequence + * of four unsigned 32 bit integers. + * + * --------------------- begin from RFC 4122 ---------------------- + * Consider each field of the UUID to be an unsigned integer as shown + * in the table in section Section 4.1.2. Then, to compare a pair of + * UUIDs, arithmetically compare the corresponding fields from each + * UUID in order of significance and according to their data type. + * Two UUIDs are equal if and only if all the corresponding fields + * are equal. + * + * UUIDs, as defined in this document, can also be ordered + * lexicographically. For a pair of UUIDs, the first one follows the + * second if the most significant field in which the UUIDs differ is + * greater for the first UUID. The second precedes the first if the + * most significant field in which the UUIDs differ is greater for + * the second UUID. + * + * The fields are encoded as 16 octets, with the sizes and order of the + * fields defined above, and with each field encoded with the Most + * Significant Byte first (known as network byte order). Note that the + * field names, particularly for multiplexed fields, follow historical + * practice. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | time_low | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | time_mid | time_hi_and_version | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |clk_seq_hi_res | clk_seq_low | node (0-1) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | node (2-5) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * ---------------------- end from RFC 4122 ----------------------- + */ + +typedef struct { + union { + /* alignment may be needed to evaluate collations */ + u32 alignment; + GUID guid; + } object_id; +} OBJECT_ID_INDEX_KEY; + +typedef struct { + le64 file_id; + GUID birth_volume_id; + GUID birth_object_id; + GUID domain_id; +} OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA + +struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */ + INDEX_ENTRY_HEADER header; + OBJECT_ID_INDEX_KEY key; + OBJECT_ID_INDEX_DATA data; +} ; + +static ntfschar objid_index_name[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('O') }; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Set the index for a new object id + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *object_id) +{ + struct OBJECT_ID_INDEX indx; + u64 file_id_cpu; + le64 file_id; + le16 seqn; + + seqn = ni->mrec->sequence_number; + file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data_offset = const_cpu_to_le16( + sizeof(INDEX_ENTRY_HEADER) + + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.data_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_DATA)); + indx.header.reservedV = const_cpu_to_le32(0); + indx.header.length = const_cpu_to_le16( + sizeof(struct OBJECT_ID_INDEX)); + indx.header.key_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.flags = const_cpu_to_le16(0); + indx.header.reserved = const_cpu_to_le16(0); + + memcpy(&indx.key.object_id,object_id,sizeof(GUID)); + + indx.data.file_id = file_id; + memcpy(&indx.data.birth_volume_id, + &object_id->birth_volume_id,sizeof(GUID)); + memcpy(&indx.data.birth_object_id, + &object_id->birth_object_id,sizeof(GUID)); + memcpy(&indx.data.domain_id, + &object_id->domain_id,sizeof(GUID)); + ntfs_index_ctx_reinit(xo); + return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx)); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Open the $Extend/$ObjId file and its index + * + * Return the index context if opened + * or NULL if an error occurred (errno tells why) + * + * The index has to be freed and inode closed when not needed any more. + */ + +static ntfs_index_context *open_object_id_index(ntfs_volume *vol) +{ + u64 inum; + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_index_context *xo; + + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } + if (ni) { + xo = ntfs_index_ctx_get(ni, objid_index_name, 2); + if (!xo) { + ntfs_inode_close(ni); + } + } else + xo = (ntfs_index_context*)NULL; + return (xo); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Merge object_id data stored in the index into + * a full object_id struct. + * + * returns 0 if merging successful + * -1 if no data could be merged. This is generally not an error + */ + +static int merge_index_data(ntfs_inode *ni, + const OBJECT_ID_ATTR *objectid_attr, + OBJECT_ID_ATTR *full_objectid) +{ + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + ntfs_index_context *xo; + ntfs_inode *xoni; + int res; + + res = -1; + xo = open_object_id_index(ni->vol); + if (xo) { + memcpy(&key.object_id,objectid_attr,sizeof(GUID)); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + /* make sure inode numbers match */ + if (entry + && (MREF(le64_to_cpu(entry->data.file_id)) + == ni->mft_no)) { + memcpy(&full_objectid->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&full_objectid->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&full_objectid->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + res = 0; + } + } + xoni = xo->ni; + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Remove an object id index entry if attribute present + * + * Returns the size of existing object id + * (the existing object_d is returned) + * -1 if failure, explained by errno + */ + +static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, + OBJECT_ID_ATTR *old_attr) +{ + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + s64 size; + int ret; + + ret = na->data_size; + if (ret) { + /* read the existing object id attribute */ + size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr); + if (size >= (s64)sizeof(GUID)) { + memcpy(&key.object_id, + &old_attr->object_id,sizeof(GUID)); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + memcpy(&old_attr->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&old_attr->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&old_attr->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + if (ntfs_index_rm(xo)) + ret = -1; + } + } else { + ret = -1; + errno = ENODATA; + } + } + return (ret); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Update the object id and index + * + * The object_id attribute should have been created and the + * non-duplication of the GUID should have been checked before. + * + * Returns 0 if success + * -1 if failure, explained by errno + * If could not remove the existing index, nothing is done, + * If could not write the new data, no index entry is inserted + * If failed to insert the index, data is removed + */ + +static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *value, size_t size) +{ + OBJECT_ID_ATTR old_attr; + ntfs_attr *na; + int oldsize; + int written; + int res; + + res = 0; + + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { + + /* remove the existing index entry */ + oldsize = remove_object_id_index(na,xo,&old_attr); + if (oldsize < 0) + res = -1; + else { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)sizeof(GUID)); + /* write the object_id in attribute */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)sizeof(GUID), + &value->object_id); + if (written != (s64)sizeof(GUID)) { + ntfs_log_error("Failed to update " + "object id\n"); + errno = EIO; + res = -1; + } + } + /* write index part if provided */ + if (!res + && ((size < sizeof(OBJECT_ID_ATTR)) + || set_object_id_index(ni,xo,value))) { + /* + * If cannot index, try to remove the object + * id and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(na); + ntfs_log_error("Failed to index object id." + " Possible corruption.\n"); + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + return (res); +} + +/* + * Add a (dummy) object id to an inode if it does not exist + * + * returns 0 if attribute was inserted (or already present) + * -1 if adding failed (explained by errno) + */ + +static int add_object_id(ntfs_inode *ni, int flags) +{ + int res; + u8 dummy; + + res = -1; /* default return */ + if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no object id attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, AT_OBJECT_ID, + AT_UNNAMED, 0, &dummy, (s64)0); + NInoSetDirty(ni); + } else + errno = EOPNOTSUPP; + } else + errno = ENODATA; + } else { + if (flags & XATTR_CREATE) + errno = EEXIST; + else + res = 0; + } + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Delete an object_id index entry + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +int ntfs_delete_object_id_index(ntfs_inode *ni) +{ + ntfs_index_context *xo; + ntfs_inode *xoni; + ntfs_attr *na; + OBJECT_ID_ATTR old_attr; + int res; + + res = 0; + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { + /* + * read the existing object id + * and un-index it + */ + xo = open_object_id_index(ni->vol); + if (xo) { + if (remove_object_id_index(na,xo,&old_attr) < 0) + res = -1; + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + ntfs_attr_close(na); + } + return (res); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get the ntfs object id into an extended attribute + * + * If present, the object_id from the attribute and the GUIDs + * from the index are returned (formatted as OBJECT_ID_ATTR) + * + * Returns the global size (can be 0, 16 or 64) + * and the buffer is updated if it is long enough + */ + +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size) +{ + OBJECT_ID_ATTR full_objectid; + OBJECT_ID_ATTR *objectid_attr; + s64 attr_size; + int full_size; + + full_size = 0; /* default to no data and some error to be defined */ + if (ni) { + objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni, + AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size); + if (objectid_attr) { + /* restrict to only GUID present in attr */ + if (attr_size == sizeof(GUID)) { + memcpy(&full_objectid.object_id, + objectid_attr,sizeof(GUID)); + full_size = sizeof(GUID); + /* get data from index, if any */ + if (!merge_index_data(ni, objectid_attr, + &full_objectid)) { + full_size = sizeof(OBJECT_ID_ATTR); + } + if (full_size <= (s64)size) { + if (value) + memcpy(value,&full_objectid, + full_size); + else + errno = EINVAL; + } + } else { + /* unexpected size, better return unsupported */ + errno = EOPNOTSUPP; + full_size = 0; + } + free(objectid_attr); + } else + errno = ENODATA; + } + return (full_size ? (int)full_size : -errno); +} + +/* + * Set the object id from an extended attribute + * + * If the size is 64, the attribute and index are set. + * else if the size is not less than 16 only the attribute is set. + * The object id index is set accordingly. + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_object_id(ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + OBJECT_ID_INDEX_KEY key; + ntfs_inode *xoni; + ntfs_index_context *xo; + int res; + + res = 0; + if (ni && value && (size >= sizeof(GUID))) { + xo = open_object_id_index(ni->vol); + if (xo) { + /* make sure the GUID was not used somewhere */ + memcpy(&key.object_id, value, sizeof(GUID)); + if (ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + ntfs_index_ctx_reinit(xo); + res = add_object_id(ni, flags); + if (!res) { + /* update value and index */ + res = update_object_id(ni,xo, + (const OBJECT_ID_ATTR*)value, + size); + } + } else { + /* GUID is present elsewhere */ + res = -1; + errno = EEXIST; + } + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } else { + res = -1; + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Remove the object id + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_remove_ntfs_object_id(ntfs_inode *ni) +{ + int res; + int olderrno; + ntfs_attr *na; + ntfs_inode *xoni; + ntfs_index_context *xo; + int oldsize; + OBJECT_ID_ATTR old_attr; + + res = 0; + if (ni) { + /* + * open and delete the object id + */ + na = ntfs_attr_open(ni, AT_OBJECT_ID, + AT_UNNAMED,0); + if (na) { + /* first remove index (old object id needed) */ + xo = open_object_id_index(ni->vol); + if (xo) { + oldsize = remove_object_id_index(na,xo, + &old_attr); + if (oldsize < 0) { + res = -1; + } else { + /* now remove attribute */ + res = ntfs_attr_rm(na); + if (res + && (oldsize > (int)sizeof(GUID))) { + /* + * If we could not remove the + * attribute, try to restore the + * index and log the error. There + * will be an inconsistency if + * the reindexing fails. + */ + set_object_id_index(ni, xo, + &old_attr); + ntfs_log_error( + "Failed to remove object id." + " Possible corruption.\n"); + } + } + + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + olderrno = errno; + ntfs_attr_close(na); + /* avoid errno pollution */ + if (errno == ENOENT) + errno = olderrno; + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ diff --git a/libntfs-3g/realpath.c b/libntfs-3g/realpath.c new file mode 100755 index 0000000000000000000000000000000000000000..a93bc698eea6d259caa661693d53a7c1fe5f21a0 --- /dev/null +++ b/libntfs-3g/realpath.c @@ -0,0 +1,103 @@ +/* + * realpath.c - realpath() aware of device mapper + * Originated from the util-linux project. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif + +#include "param.h" +#include "realpath.h" + +/* If there is no realpath() on the system, provide a dummy one. */ +#ifndef HAVE_REALPATH +char *ntfs_realpath(const char *path, char *resolved_path) +{ + strncpy(resolved_path, path, PATH_MAX); + resolved_path[PATH_MAX] = '\0'; + return resolved_path; +} +#endif + + +#ifdef linux + +/* + * Converts private "dm-N" names to "/dev/mapper/<name>" + * + * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs + * provides the real DM device names in /sys/block/<ptname>/dm/name + */ +static char * +canonicalize_dm_name(const char *ptname, char *canonical) +{ + FILE *f; + size_t sz; + char path[MAPPERNAMELTH + 24]; + char name[MAPPERNAMELTH + 16]; + char *res = NULL; + + snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname); + if (!(f = fopen(path, "r"))) + return NULL; + + /* read "<name>\n" from sysfs */ + if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) { + name[sz - 1] = '\0'; + snprintf(path, sizeof(path), "/dev/mapper/%s", name); + res = strcpy(canonical, path); + } + fclose(f); + return res; +} + +/* + * Canonicalize a device path + * + * Workaround from "basinilya" for fixing device mapper paths. + * + * Background (Phillip Susi, 2011-04-09) + * - ntfs-3g canonicalizes the device name so that if you mount with + * /dev/mapper/foo, the device name listed in mtab is /dev/dm-n, + * so you can not umount /dev/mapper/foo + * - umount won't even recognize and translate /dev/dm-n to the mount + * point, apparently because of the '-' involved. Editing mtab and + * removing the '-' allows you to umount /dev/dmn successfully. + * + * This code restores the devmapper name after canonicalization, + * until a proper fix is implemented. + */ + +char *ntfs_realpath_canonicalize(const char *path, char *canonical) +{ + char *p; + + if (path == NULL) + return NULL; + + if (!ntfs_realpath(path, canonical)) + return NULL; + + p = strrchr(canonical, '/'); + if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) { + p = canonicalize_dm_name(p+1, canonical); + if (p) + return p; + } + + return canonical; +} + +#endif diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c new file mode 100755 index 0000000000000000000000000000000000000000..7b96902c50fc26e70c86edcb2b7f9f5f7d2e41cb --- /dev/null +++ b/libntfs-3g/reparse.c @@ -0,0 +1,1253 @@ +/** + * reparse.c - Processing of reparse points + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2008-2014 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include <sys/sysmacros.h> +#endif + +#include "compat.h" +#include "types.h" +#include "debug.h" +#include "layout.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "lcnalloc.h" +#include "logging.h" +#include "misc.h" +#include "reparse.h" + +struct MOUNT_POINT_REPARSE_DATA { /* reparse data for junctions */ + le16 subst_name_offset; + le16 subst_name_length; + le16 print_name_offset; + le16 print_name_length; + char path_buffer[0]; /* above data assume this is char array */ +} ; + +struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */ + le16 subst_name_offset; + le16 subst_name_length; + le16 print_name_offset; + le16 print_name_length; + le32 flags; /* 1 for full target, otherwise 0 */ + char path_buffer[0]; /* above data assume this is char array */ +} ; + +struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */ + INDEX_ENTRY_HEADER header; + REPARSE_INDEX_KEY key; + le32 filling; +} ; + +static const ntfschar dir_junction_head[] = { + const_cpu_to_le16('\\'), + const_cpu_to_le16('?'), + const_cpu_to_le16('?'), + const_cpu_to_le16('\\') +} ; + +static const ntfschar vol_junction_head[] = { + const_cpu_to_le16('\\'), + const_cpu_to_le16('?'), + const_cpu_to_le16('?'), + const_cpu_to_le16('\\'), + const_cpu_to_le16('V'), + const_cpu_to_le16('o'), + const_cpu_to_le16('l'), + const_cpu_to_le16('u'), + const_cpu_to_le16('m'), + const_cpu_to_le16('e'), + const_cpu_to_le16('{'), +} ; + +static ntfschar reparse_index_name[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('R') }; + +static const char mappingdir[] = ".NTFS-3G/"; + +/* + * Fix a file name with doubtful case in some directory index + * and return the name with the casing used in directory. + * + * Should only be used to translate paths stored with case insensitivity + * (such as directory junctions) when no case conflict is expected. + * If there some ambiguity, the name which collates first is returned. + * + * The name is converted to upper case and searched the usual way. + * The collation rules for file names are such that we should get the + * first candidate if any. + */ + +static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname, + int uname_len) +{ + ntfs_volume *vol = dir_ni->vol; + ntfs_index_context *icx; + u64 mref; + le64 lemref; + int lkup; + int olderrno; + int i; + u32 cpuchar; + INDEX_ENTRY *entry; + FILE_NAME_ATTR *found; + struct { + FILE_NAME_ATTR attr; + ntfschar file_name[NTFS_MAX_NAME_LEN + 1]; + } find; + + mref = (u64)-1; /* default return (not found) */ + icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); + if (icx) { + if (uname_len > NTFS_MAX_NAME_LEN) + uname_len = NTFS_MAX_NAME_LEN; + find.attr.file_name_length = uname_len; + for (i=0; i<uname_len; i++) { + cpuchar = le16_to_cpu(uname[i]); + /* + * We need upper or lower value, whichever is smaller, + * but we can only convert to upper case, so we + * will fail when searching for an upper case char + * whose lower case is smaller (such as umlauted Y) + */ + if ((cpuchar < vol->upcase_len) + && (le16_to_cpu(vol->upcase[cpuchar]) < cpuchar)) + find.attr.file_name[i] = vol->upcase[cpuchar]; + else + find.attr.file_name[i] = uname[i]; + } + olderrno = errno; + lkup = ntfs_index_lookup((char*)&find, uname_len, icx); + if (errno == ENOENT) + errno = olderrno; + /* + * We generally only get the first matching candidate, + * so we still have to check whether this is a real match + */ + if (icx->entry && (icx->entry->ie_flags & INDEX_ENTRY_END)) + /* get next entry if reaching end of block */ + entry = ntfs_index_next(icx->entry, icx); + else + entry = icx->entry; + if (entry) { + found = &entry->key.file_name; + if (lkup + && ntfs_names_are_equal(find.attr.file_name, + find.attr.file_name_length, + found->file_name, found->file_name_length, + IGNORE_CASE, + vol->upcase, vol->upcase_len)) + lkup = 0; + if (!lkup) { + /* + * name found : + * fix original name and return inode + */ + lemref = entry->indexed_file; + mref = le64_to_cpu(lemref); + if (NVolCaseSensitive(vol) || !vol->locase) { + for (i=0; i<found->file_name_length; i++) + uname[i] = found->file_name[i]; + } else { + for (i=0; i<found->file_name_length; i++) + uname[i] = vol->locase[found->file_name[i]]; + } + } + } + ntfs_index_ctx_put(icx); + } + return (mref); +} + +/* + * Search for a directory junction or a symbolic link + * along the target path, with target defined as a full absolute path + * + * Returns the path translated to a Linux path + * or NULL if the path is not valid + */ + +static char *search_absolute(ntfs_volume *vol, ntfschar *path, + int count, BOOL isdir) +{ + ntfs_inode *ni; + u64 inum; + char *target; + int start; + int len; + + target = (char*)NULL; /* default return */ + ni = ntfs_inode_open(vol, (MFT_REF)FILE_root); + if (ni) { + start = 0; + /* + * Examine and translate the path, until we reach either + * - the end, + * - an unknown item + * - a non-directory + * - another reparse point, + * A reparse point is not dereferenced, it will be + * examined later when the translated path is dereferenced, + * however the final part of the path will not be adjusted + * to correct case. + */ + do { + len = 0; + while (((start + len) < count) + && (path[start + len] != const_cpu_to_le16('\\'))) + len++; + inum = ntfs_fix_file_name(ni, &path[start], len); + ntfs_inode_close(ni); + ni = (ntfs_inode*)NULL; + if (inum != (u64)-1) { + inum = MREF(inum); + ni = ntfs_inode_open(vol, inum); + start += len; + if (start < count) + path[start++] = const_cpu_to_le16('/'); + } + } while (ni + && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + && !(ni->flags & FILE_ATTR_REPARSE_POINT) + && (start < count)); + if (ni + && ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? isdir : !isdir) + || (ni->flags & FILE_ATTR_REPARSE_POINT))) + if (ntfs_ucstombs(path, count, &target, 0) < 0) { + if (target) { + free(target); + target = (char*)NULL; + } + } + if (ni) + ntfs_inode_close(ni); + } + return (target); +} + +/* + * Search for a symbolic link along the target path, + * with the target defined as a relative path + * + * Note : the path used to access the current inode, may be + * different from the one implied in the target definition, + * when an inode has names in several directories. + * + * Returns the path translated to a Linux path + * or NULL if the path is not valid + */ + +static char *search_relative(ntfs_inode *ni, ntfschar *path, int count) +{ + char *target = (char*)NULL; + ntfs_inode *curni; + ntfs_inode *newni; + u64 inum; + int pos; + int lth; + BOOL ok; + BOOL morelinks; + int max = 32; /* safety */ + + pos = 0; + ok = TRUE; + morelinks = FALSE; + curni = ntfs_dir_parent_inode(ni); + /* + * Examine and translate the path, until we reach either + * - the end, + * - an unknown item + * - a non-directory + * - another reparse point, + * A reparse point is not dereferenced, it will be + * examined later when the translated path is dereferenced, + * however the final part of the path will not be adjusted + * to correct case. + */ + while (curni && ok && !morelinks && (pos < (count - 1)) && --max) { + if ((count >= (pos + 2)) + && (path[pos] == const_cpu_to_le16('.')) + && (path[pos+1] == const_cpu_to_le16('\\'))) { + path[pos+1] = const_cpu_to_le16('/'); + pos += 2; + } else { + if ((count >= (pos + 3)) + && (path[pos] == const_cpu_to_le16('.')) + &&(path[pos+1] == const_cpu_to_le16('.')) + && (path[pos+2] == const_cpu_to_le16('\\'))) { + path[pos+2] = const_cpu_to_le16('/'); + pos += 3; + newni = ntfs_dir_parent_inode(curni); + if (curni != ni) + ntfs_inode_close(curni); + curni = newni; + if (!curni) + ok = FALSE; + } else { + lth = 0; + while (((pos + lth) < count) + && (path[pos + lth] != const_cpu_to_le16('\\'))) + lth++; + if (lth > 0) + inum = ntfs_fix_file_name(curni,&path[pos],lth); + else + inum = (u64)-1; + if (!lth + || ((curni != ni) + && ntfs_inode_close(curni)) + || (inum == (u64)-1)) + ok = FALSE; + else { + curni = ntfs_inode_open(ni->vol, MREF(inum)); + if (!curni) + ok = FALSE; + else { + if (curni->flags & FILE_ATTR_REPARSE_POINT) + morelinks = TRUE; + if (ok && ((pos + lth) < count)) { + path[pos + lth] = const_cpu_to_le16('/'); + pos += lth + 1; + if (morelinks + && ntfs_inode_close(curni)) + ok = FALSE; + } else { + pos += lth; + if (!morelinks + && (ni->mrec->flags ^ curni->mrec->flags) + & MFT_RECORD_IS_DIRECTORY) + ok = FALSE; + if (ntfs_inode_close(curni)) + ok = FALSE; + } + } + } + } + } + } + + if (ok && (ntfs_ucstombs(path, count, &target, 0) < 0)) { + free(target); // needed ? + target = (char*)NULL; + } + return (target); +} + +/* + * Check whether a drive letter has been defined in .NTFS-3G + * + * Returns 1 if found, + * 0 if not found, + * -1 if there was an error (described by errno) + */ + +static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter) +{ + char defines[NTFS_MAX_NAME_LEN + 5]; + char *drive; + int ret; + int sz; + int olderrno; + ntfs_inode *ni; + + ret = -1; + drive = (char*)NULL; + sz = ntfs_ucstombs(&letter, 1, &drive, 0); + if (sz > 0) { + strcpy(defines,mappingdir); + if ((*drive >= 'a') && (*drive <= 'z')) + *drive += 'A' - 'a'; + strcat(defines,drive); + strcat(defines,":"); + olderrno = errno; + ni = ntfs_pathname_to_inode(vol, NULL, defines); + if (ni && !ntfs_inode_close(ni)) + ret = 1; + else + if (errno == ENOENT) { + ret = 0; + /* avoid errno pollution */ + errno = olderrno; + } + } + if (drive) + free(drive); + return (ret); +} + +/* + * Do some sanity checks on reparse data + * + * The only general check is about the size (at least the tag must + * be present) + * If the reparse data looks like a junction point or symbolic + * link, more checks can be done. + * + */ + +static BOOL valid_reparse_data(ntfs_inode *ni, + const REPARSE_POINT *reparse_attr, size_t size) +{ + BOOL ok; + unsigned int offs; + unsigned int lth; + const struct MOUNT_POINT_REPARSE_DATA *mount_point_data; + const struct SYMLINK_REPARSE_DATA *symlink_data; + + ok = ni && reparse_attr + && (size >= sizeof(REPARSE_POINT)) + && (((size_t)le16_to_cpu(reparse_attr->reparse_data_length) + + sizeof(REPARSE_POINT)) == size); + if (ok) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + mount_point_data = (const struct MOUNT_POINT_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(mount_point_data->subst_name_offset); + lth = le16_to_cpu(mount_point_data->subst_name_length); + /* consistency checks */ + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + || ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct MOUNT_POINT_REPARSE_DATA) + + offs + lth)) > size)) + ok = FALSE; + break; + case IO_REPARSE_TAG_SYMLINK : + symlink_data = (const struct SYMLINK_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(symlink_data->subst_name_offset); + lth = le16_to_cpu(symlink_data->subst_name_length); + if ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct SYMLINK_REPARSE_DATA) + + offs + lth)) > size) + ok = FALSE; + break; + default : + break; + } + } + if (!ok) + errno = EINVAL; + return (ok); +} + +/* + * Check and translate the target of a junction point or + * a full absolute symbolic link. + * + * A full target definition begins with "\??\" or "\\?\" + * + * The fully defined target is redefined as a relative link, + * - either to the target if found on the same device. + * - or into the /.NTFS-3G directory for the user to define + * In the first situation, the target is translated to case-sensitive path. + * + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno + */ + +static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction, + int count, const char *mnt_point, BOOL isdir) +{ + char *target; + char *fulltarget; + int sz; + char *q; + enum { DIR_JUNCTION, VOL_JUNCTION, NO_JUNCTION } kind; + + target = (char*)NULL; + fulltarget = (char*)NULL; + /* + * For a valid directory junction we want \??\x:\ + * where \ is an individual char and x a non-null char + */ + if ((count >= 7) + && !memcmp(junction,dir_junction_head,8) + && junction[4] + && (junction[5] == const_cpu_to_le16(':')) + && (junction[6] == const_cpu_to_le16('\\'))) + kind = DIR_JUNCTION; + else + /* + * For a valid volume junction we want \\?\Volume{ + * and a final \ (where \ is an individual char) + */ + if ((count >= 12) + && !memcmp(junction,vol_junction_head,22) + && (junction[count-1] == const_cpu_to_le16('\\'))) + kind = VOL_JUNCTION; + else + kind = NO_JUNCTION; + /* + * Directory junction with an explicit path and + * no specific definition for the drive letter : + * try to interpret as a target on the same volume + */ + if ((kind == DIR_JUNCTION) + && (count >= 7) + && junction[7] + && !ntfs_drive_letter(vol, junction[4])) { + target = search_absolute(vol,&junction[7],count - 7, isdir); + if (target) { + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + strlen(target) + 2); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,target); + } + free(target); + } + } + /* + * Volume junctions or directory junctions with + * target not found on current volume : + * link to /.NTFS-3G/target which the user can + * define as a symbolic link to the real target + */ + if (((kind == DIR_JUNCTION) && !fulltarget) + || (kind == VOL_JUNCTION)) { + sz = ntfs_ucstombs(&junction[4], + (kind == VOL_JUNCTION ? count - 5 : count - 4), + &target, 0); + if ((sz > 0) && target) { + /* reverse slashes */ + for (q=target; *q; q++) + if (*q == '\\') + *q = '/'; + /* force uppercase drive letter */ + if ((target[1] == ':') + && (target[0] >= 'a') + && (target[0] <= 'z')) + target[0] += 'A' - 'a'; + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,mappingdir); + strcat(fulltarget,target); + } + } + if (target) + free(target); + } + return (fulltarget); +} + +/* + * Check and translate the target of an absolute symbolic link. + * + * An absolute target definition begins with "\" or "x:\" + * + * The absolute target is redefined as a relative link, + * - either to the target if found on the same device. + * - or into the /.NTFS-3G directory for the user to define + * In the first situation, the target is translated to case-sensitive path. + * + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno + */ + +static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, + int count, const char *mnt_point, BOOL isdir) +{ + char *target; + char *fulltarget; + int sz; + char *q; + enum { FULL_PATH, ABS_PATH, REJECTED_PATH } kind; + + target = (char*)NULL; + fulltarget = (char*)NULL; + /* + * For a full valid path we want x:\ + * where \ is an individual char and x a non-null char + */ + if ((count >= 3) + && junction[0] + && (junction[1] == const_cpu_to_le16(':')) + && (junction[2] == const_cpu_to_le16('\\'))) + kind = FULL_PATH; + else + /* + * For an absolute path we want an initial \ + */ + if ((count >= 0) + && (junction[0] == const_cpu_to_le16('\\'))) + kind = ABS_PATH; + else + kind = REJECTED_PATH; + /* + * Full path, with a drive letter and + * no specific definition for the drive letter : + * try to interpret as a target on the same volume. + * Do the same for an abs path with no drive letter. + */ + if (((kind == FULL_PATH) + && (count >= 3) + && junction[3] + && !ntfs_drive_letter(vol, junction[0])) + || (kind == ABS_PATH)) { + if (kind == ABS_PATH) + target = search_absolute(vol, &junction[1], + count - 1, isdir); + else + target = search_absolute(vol, &junction[3], + count - 3, isdir); + if (target) { + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + strlen(target) + 2); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,target); + } + free(target); + } + } + /* + * full path with target not found on current volume : + * link to /.NTFS-3G/target which the user can + * define as a symbolic link to the real target + */ + if ((kind == FULL_PATH) && !fulltarget) { + sz = ntfs_ucstombs(&junction[0], + count,&target, 0); + if ((sz > 0) && target) { + /* reverse slashes */ + for (q=target; *q; q++) + if (*q == '\\') + *q = '/'; + /* force uppercase drive letter */ + if ((target[1] == ':') + && (target[0] >= 'a') + && (target[0] <= 'z')) + target[0] += 'A' - 'a'; + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); + if (fulltarget) { + strcpy(fulltarget,mnt_point); + strcat(fulltarget,"/"); + strcat(fulltarget,mappingdir); + strcat(fulltarget,target); + } + } + if (target) + free(target); + } + return (fulltarget); +} + +/* + * Check and translate the target of a relative symbolic link. + * + * A relative target definition does not begin with "\" + * + * The original definition of relative target is kept, it is just + * translated to a case-sensitive path. + * + * returns the target converted to a relative symlink + * or NULL if there were some problem, as described by errno + */ + +static char *ntfs_get_rellink(ntfs_inode *ni, ntfschar *junction, int count) +{ + char *target; + + target = search_relative(ni,junction,count); + return (target); +} + +/* + * Get the target for a junction point or symbolic link + * Should only be called for files or directories with reparse data + * + * returns the target converted to a relative path, or NULL + * if some error occurred, as described by errno + * errno is EOPNOTSUPP if the reparse point is not a valid + * symbolic link or directory junction + */ + +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, + int *pattr_size) +{ + s64 attr_size = 0; + char *target; + unsigned int offs; + unsigned int lth; + ntfs_volume *vol; + REPARSE_POINT *reparse_attr; + struct MOUNT_POINT_REPARSE_DATA *mount_point_data; + struct SYMLINK_REPARSE_DATA *symlink_data; + enum { FULL_TARGET, ABS_TARGET, REL_TARGET } kind; + ntfschar *p; + BOOL bad; + BOOL isdir; + + target = (char*)NULL; + bad = TRUE; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + vol = ni->vol; + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr && attr_size + && valid_reparse_data(ni, reparse_attr, attr_size)) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + mount_point_data = (struct MOUNT_POINT_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(mount_point_data->subst_name_offset); + lth = le16_to_cpu(mount_point_data->subst_name_length); + /* reparse data consistency has been checked */ + target = ntfs_get_fulllink(vol, + (ntfschar*)&mount_point_data->path_buffer[offs], + lth/2, mnt_point, isdir); + if (target) + bad = FALSE; + break; + case IO_REPARSE_TAG_SYMLINK : + symlink_data = (struct SYMLINK_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(symlink_data->subst_name_offset); + lth = le16_to_cpu(symlink_data->subst_name_length); + p = (ntfschar*)&symlink_data->path_buffer[offs]; + /* + * Predetermine the kind of target, + * the called function has to make a full check + */ + if (*p++ == const_cpu_to_le16('\\')) { + if ((*p == const_cpu_to_le16('?')) + || (*p == const_cpu_to_le16('\\'))) + kind = FULL_TARGET; + else + kind = ABS_TARGET; + } else + if (*p == const_cpu_to_le16(':')) + kind = ABS_TARGET; + else + kind = REL_TARGET; + p--; + /* reparse data consistency has been checked */ + switch (kind) { + case FULL_TARGET : + if (!(symlink_data->flags + & const_cpu_to_le32(1))) { + target = ntfs_get_fulllink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; + } + break; + case ABS_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_abslink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; + } + break; + case REL_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_rellink(ni, + p, lth/2); + if (target) + bad = FALSE; + } + break; + } + break; + } + free(reparse_attr); + } + *pattr_size = attr_size; + if (bad) + errno = EOPNOTSUPP; + return (target); +} + +/* + * Check whether a reparse point looks like a junction point + * or a symbolic link. + * Should only be called for files or directories with reparse data + * + * The validity of the target is not checked. + */ + +BOOL ntfs_possible_symlink(ntfs_inode *ni) +{ + s64 attr_size = 0; + REPARSE_POINT *reparse_attr; + BOOL possible; + + possible = FALSE; + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr && attr_size) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + case IO_REPARSE_TAG_SYMLINK : + possible = TRUE; + default : ; + } + free(reparse_attr); + } + return (possible); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Set the index for new reparse data + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +static int set_reparse_index(ntfs_inode *ni, ntfs_index_context *xr, + le32 reparse_tag) +{ + struct REPARSE_INDEX indx; + u64 file_id_cpu; + le64 file_id; + le16 seqn; + + seqn = ni->mrec->sequence_number; + file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data_offset = const_cpu_to_le16( + sizeof(INDEX_ENTRY_HEADER) + + sizeof(REPARSE_INDEX_KEY)); + indx.header.data_length = const_cpu_to_le16(0); + indx.header.reservedV = const_cpu_to_le32(0); + indx.header.length = const_cpu_to_le16( + sizeof(struct REPARSE_INDEX)); + indx.header.key_length = const_cpu_to_le16( + sizeof(REPARSE_INDEX_KEY)); + indx.header.flags = const_cpu_to_le16(0); + indx.header.reserved = const_cpu_to_le16(0); + indx.key.reparse_tag = reparse_tag; + /* danger on processors which require proper alignment ! */ + memcpy(&indx.key.file_id, &file_id, 8); + indx.filling = const_cpu_to_le32(0); + ntfs_index_ctx_reinit(xr); + return (ntfs_ie_add(xr,(INDEX_ENTRY*)&indx)); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Remove a reparse data index entry if attribute present + * + * Returns the size of existing reparse data + * (the existing reparse tag is returned) + * -1 if failure, explained by errno + */ + +static int remove_reparse_index(ntfs_attr *na, ntfs_index_context *xr, + le32 *preparse_tag) +{ + REPARSE_INDEX_KEY key; + u64 file_id_cpu; + le64 file_id; + s64 size; + le16 seqn; + int ret; + + ret = na->data_size; + if (ret) { + /* read the existing reparse_tag */ + size = ntfs_attr_pread(na, 0, 4, preparse_tag); + if (size == 4) { + seqn = na->ni->mrec->sequence_number; + file_id_cpu = MK_MREF(na->ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + key.reparse_tag = *preparse_tag; + /* danger on processors which require proper alignment ! */ + memcpy(&key.file_id, &file_id, 8); + if (!ntfs_index_lookup(&key, sizeof(REPARSE_INDEX_KEY), xr) + && ntfs_index_rm(xr)) + ret = -1; + } else { + ret = -1; + errno = ENODATA; + } + } + return (ret); +} + +/* + * Open the $Extend/$Reparse file and its index + * + * Return the index context if opened + * or NULL if an error occurred (errno tells why) + * + * The index has to be freed and inode closed when not needed any more. + */ + +static ntfs_index_context *open_reparse_index(ntfs_volume *vol) +{ + u64 inum; + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_index_context *xr; + + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$Reparse"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } + if (ni) { + xr = ntfs_index_ctx_get(ni, reparse_index_name, 2); + if (!xr) { + ntfs_inode_close(ni); + } + } else + xr = (ntfs_index_context*)NULL; + return (xr); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Update the reparse data and index + * + * The reparse data attribute should have been created, and + * an existing index is expected if there is an existing value. + * + * Returns 0 if success + * -1 if failure, explained by errno + * If could not remove the existing index, nothing is done, + * If could not write the new data, no index entry is inserted + * If failed to insert the index, data is removed + */ + +static int update_reparse_data(ntfs_inode *ni, ntfs_index_context *xr, + const char *value, size_t size) +{ + int res; + int written; + int oldsize; + ntfs_attr *na; + le32 reparse_tag; + + res = 0; + na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); + if (na) { + /* remove the existing reparse data */ + oldsize = remove_reparse_index(na,xr,&reparse_tag); + if (oldsize < 0) + res = -1; + else { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)size); + /* overwrite value if any */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)size, value); + if (written != (s64)size) { + ntfs_log_error("Failed to update " + "reparse data\n"); + errno = EIO; + res = -1; + } + } + if (!res + && set_reparse_index(ni,xr, + ((const REPARSE_POINT*)value)->reparse_tag) + && (oldsize > 0)) { + /* + * If cannot index, try to remove the reparse + * data and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(na); + ntfs_log_error("Failed to index reparse data." + " Possible corruption.\n"); + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Delete a reparse index entry + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +int ntfs_delete_reparse_index(ntfs_inode *ni) +{ + ntfs_index_context *xr; + ntfs_inode *xrni; + ntfs_attr *na; + le32 reparse_tag; + int res; + + res = 0; + na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); + if (na) { + /* + * read the existing reparse data (the tag is enough) + * and un-index it + */ + xr = open_reparse_index(ni->vol); + if (xr) { + if (remove_reparse_index(na,xr,&reparse_tag) < 0) + res = -1; + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } + ntfs_attr_close(na); + } + return (res); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get the ntfs reparse data into an extended attribute + * + * Returns the reparse data size + * and the buffer is updated if it is long enough + */ + +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size) +{ + REPARSE_POINT *reparse_attr; + s64 attr_size; + + attr_size = 0; /* default to no data and no error */ + if (ni) { + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, + AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); + if (reparse_attr) { + if (attr_size <= (s64)size) { + if (value) + memcpy(value,reparse_attr, + attr_size); + else + errno = EINVAL; + } + free(reparse_attr); + } + } else + errno = ENODATA; + } + return (attr_size ? (int)attr_size : -errno); +} + +/* + * Set the reparse data from an extended attribute + * + * Warning : the new data is not checked + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + int res; + u8 dummy; + ntfs_inode *xrni; + ntfs_index_context *xr; + + res = 0; + /* reparse data is not compatible with EA */ + if (ni + && !ntfs_attr_exist(ni, AT_EA_INFORMATION, AT_UNNAMED, 0) + && !ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0) + && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) { + xr = open_reparse_index(ni->vol); + if (xr) { + if (!ntfs_attr_exist(ni,AT_REPARSE_POINT, + AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no reparse data attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, + AT_REPARSE_POINT, + AT_UNNAMED,0,&dummy, + (s64)0); + if (!res) { + ni->flags |= + FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); + } + NInoSetDirty(ni); + } else { + errno = EOPNOTSUPP; + res = -1; + } + } else { + errno = ENODATA; + res = -1; + } + } else { + if (flags & XATTR_CREATE) { + errno = EEXIST; + res = -1; + } + } + if (!res) { + /* update value and index */ + res = update_reparse_data(ni,xr,value,size); + } + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } else { + res = -1; + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Remove the reparse data + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni) +{ + int res; + int olderrno; + ntfs_attr *na; + ntfs_inode *xrni; + ntfs_index_context *xr; + le32 reparse_tag; + + res = 0; + if (ni) { + /* + * open and delete the reparse data + */ + na = ntfs_attr_open(ni, AT_REPARSE_POINT, + AT_UNNAMED,0); + if (na) { + /* first remove index (reparse data needed) */ + xr = open_reparse_index(ni->vol); + if (xr) { + if (remove_reparse_index(na,xr, + &reparse_tag) < 0) { + res = -1; + } else { + /* now remove attribute */ + res = ntfs_attr_rm(na); + if (!res) { + ni->flags &= + ~FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); + } else { + /* + * If we could not remove the + * attribute, try to restore the + * index and log the error. There + * will be an inconsistency if + * the reindexing fails. + */ + set_reparse_index(ni, xr, + reparse_tag); + ntfs_log_error( + "Failed to remove reparse data." + " Possible corruption.\n"); + } + } + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } + olderrno = errno; + ntfs_attr_close(na); + /* avoid errno pollution */ + if (errno == ENOENT) + errno = olderrno; + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ diff --git a/libntfs-3g/runlist.c b/libntfs-3g/runlist.c new file mode 100755 index 0000000000000000000000000000000000000000..7e158d443a33d93772db3a0cc752074195fd768d --- /dev/null +++ b/libntfs-3g/runlist.c @@ -0,0 +1,2174 @@ +/** + * runlist.c - Run list handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2002-2008 Szabolcs Szakacsits + * Copyright (c) 2004 Yura Pakhuchiy + * Copyright (c) 2007-2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "compat.h" +#include "types.h" +#include "volume.h" +#include "layout.h" +#include "debug.h" +#include "device.h" +#include "logging.h" +#include "misc.h" + +/** + * ntfs_rl_mm - runlist memmove + * @base: + * @dst: + * @src: + * @size: + * + * Description... + * + * Returns: + */ +static void ntfs_rl_mm(runlist_element *base, int dst, int src, int size) +{ + if ((dst != src) && (size > 0)) + memmove(base + dst, base + src, size * sizeof(*base)); +} + +/** + * ntfs_rl_mc - runlist memory copy + * @dstbase: + * @dst: + * @srcbase: + * @src: + * @size: + * + * Description... + * + * Returns: + */ +static void ntfs_rl_mc(runlist_element *dstbase, int dst, + runlist_element *srcbase, int src, int size) +{ + if (size > 0) + memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase)); +} + +/** + * ntfs_rl_realloc - Reallocate memory for runlists + * @rl: original runlist + * @old_size: number of runlist elements in the original runlist @rl + * @new_size: number of runlist elements we need space for + * + * As the runlists grow, more memory will be required. To prevent large + * numbers of small reallocations of memory, this function returns a 4kiB block + * of memory. + * + * N.B. If the new allocation doesn't require a different number of 4kiB + * blocks in memory, the function will return the original pointer. + * + * On success, return a pointer to the newly allocated, or recycled, memory. + * On error, return NULL with errno set to the error code. + */ +static runlist_element *ntfs_rl_realloc(runlist_element *rl, int old_size, + int new_size) +{ + old_size = (old_size * sizeof(runlist_element) + 0xfff) & ~0xfff; + new_size = (new_size * sizeof(runlist_element) + 0xfff) & ~0xfff; + if (old_size == new_size) + return rl; + return realloc(rl, new_size); +} + +/* + * Extend a runlist by some entry count + * The runlist may have to be reallocated + * + * Returns the reallocated runlist + * or NULL if reallocation was not possible (with errno set) + * the runlist is left unchanged if the reallocation fails + */ + +runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries) +{ + runlist_element *newrl; + int last; + int irl; + + if (na->rl && rl) { + irl = (int)(rl - na->rl); + last = irl; + while (na->rl[last].length) + last++; + newrl = ntfs_rl_realloc(na->rl,last+1,last+more_entries+1); + if (!newrl) { + errno = ENOMEM; + rl = (runlist_element*)NULL; + } else { + na->rl = newrl; + rl = &newrl[irl]; + } + } else { + ntfs_log_error("Cannot extend unmapped runlist"); + errno = EIO; + rl = (runlist_element*)NULL; + } + return (rl); +} + +/** + * ntfs_rl_are_mergeable - test if two runlists can be joined together + * @dst: original runlist + * @src: new runlist to test for mergeability with @dst + * + * Test if two runlists can be joined together. For this, their VCNs and LCNs + * must be adjacent. + * + * Return: TRUE Success, the runlists can be merged. + * FALSE Failure, the runlists cannot be merged. + */ +static BOOL ntfs_rl_are_mergeable(runlist_element *dst, runlist_element *src) +{ + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_are_mergeable() invoked with NULL " + "pointer!\n"); + return FALSE; + } + + /* We can merge unmapped regions even if they are misaligned. */ + if ((dst->lcn == LCN_RL_NOT_MAPPED) && (src->lcn == LCN_RL_NOT_MAPPED)) + return TRUE; + /* If the runs are misaligned, we cannot merge them. */ + if ((dst->vcn + dst->length) != src->vcn) + return FALSE; + /* If both runs are non-sparse and contiguous, we can merge them. */ + if ((dst->lcn >= 0) && (src->lcn >= 0) && + ((dst->lcn + dst->length) == src->lcn)) + return TRUE; + /* If we are merging two holes, we can merge them. */ + if ((dst->lcn == LCN_HOLE) && (src->lcn == LCN_HOLE)) + return TRUE; + /* Cannot merge. */ + return FALSE; +} + +/** + * __ntfs_rl_merge - merge two runlists without testing if they can be merged + * @dst: original, destination runlist + * @src: new runlist to merge with @dst + * + * Merge the two runlists, writing into the destination runlist @dst. The + * caller must make sure the runlists can be merged or this will corrupt the + * destination runlist. + */ +static void __ntfs_rl_merge(runlist_element *dst, runlist_element *src) +{ + dst->length += src->length; +} + +/** + * ntfs_rl_append - append a runlist after a given element + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: runlist to be inserted into @dst + * @ssize: number of elements in @src (excluding end marker) + * @loc: append the new runlist @src after this element in @dst + * + * Append the runlist @src after element @loc in @dst. Merge the right end of + * the new runlist, if necessary. Adjust the size of the hole before the + * appended runlist. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_append(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) +{ + BOOL right = FALSE; /* Right end of @src needs merging */ + int marker; /* End of the inserted runs */ + + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_append() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* First, check if the right hand end needs merging. */ + if ((loc + 1) < dsize) + right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); + + /* Space required: @dst size + @src size, less one if we merged. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right); + if (!dst) + return NULL; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* First, merge the right hand end, if necessary. */ + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); + + /* marker - First run after the @src runs that have been inserted */ + marker = loc + ssize + 1; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, loc + 1 + right, dsize - loc - 1 - right); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); + + /* Adjust the size of the preceding hole. */ + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; + + /* We may have changed the length of the file, so fix the end marker */ + if (dst[marker].lcn == LCN_ENOENT) + dst[marker].vcn = dst[marker-1].vcn + dst[marker-1].length; + + return dst; +} + +/** + * ntfs_rl_insert - insert a runlist into another + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: insert the new runlist @src before this element in @dst + * + * Insert the runlist @src before element @loc in the runlist @dst. Merge the + * left end of the new runlist, if necessary. Adjust the size of the hole + * after the inserted runlist. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_insert(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) +{ + BOOL left = FALSE; /* Left end of @src needs merging */ + BOOL disc = FALSE; /* Discontinuity between @dst and @src */ + int marker; /* End of the inserted runs */ + + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_insert() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* disc => Discontinuity between the end of @dst and the start of @src. + * This means we might need to insert a "notmapped" run. + */ + if (loc == 0) + disc = (src[0].vcn > 0); + else { + s64 merged_length; + + left = ntfs_rl_are_mergeable(dst + loc - 1, src); + + merged_length = dst[loc - 1].length; + if (left) + merged_length += src->length; + + disc = (src[0].vcn > dst[loc - 1].vcn + merged_length); + } + + /* Space required: @dst size + @src size, less one if we merged, plus + * one if there was a discontinuity. + */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc); + if (!dst) + return NULL; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlist. + */ + + if (left) + __ntfs_rl_merge(dst + loc - 1, src); + + /* + * marker - First run after the @src runs that have been inserted + * Nominally: marker = @loc + @ssize (location + number of runs in @src) + * If "left", then the first run in @src has been merged with one in @dst. + * If "disc", then @dst and @src don't meet and we need an extra run to fill the gap. + */ + marker = loc + ssize - left + disc; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, loc, dsize - loc); + ntfs_rl_mc(dst, loc + disc, src, left, ssize - left); + + /* Adjust the VCN of the first run after the insertion ... */ + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; + /* ... and the length. */ + if (dst[marker].lcn == LCN_HOLE || dst[marker].lcn == LCN_RL_NOT_MAPPED) + dst[marker].length = dst[marker + 1].vcn - dst[marker].vcn; + + /* Writing beyond the end of the file and there's a discontinuity. */ + if (disc) { + if (loc > 0) { + dst[loc].vcn = dst[loc - 1].vcn + dst[loc - 1].length; + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; + } else { + dst[loc].vcn = 0; + dst[loc].length = dst[loc + 1].vcn; + } + dst[loc].lcn = LCN_RL_NOT_MAPPED; + } + return dst; +} + +/** + * ntfs_rl_replace - overwrite a runlist element with another runlist + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: index in runlist @dst to overwrite with @src + * + * Replace the runlist element @dst at @loc with @src. Merge the left and + * right ends of the inserted runlist, if necessary. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_replace(runlist_element *dst, int dsize, + runlist_element *src, int ssize, + int loc) +{ + signed delta; + BOOL left = FALSE; /* Left end of @src needs merging */ + BOOL right = FALSE; /* Right end of @src needs merging */ + int tail; /* Start of tail of @dst */ + int marker; /* End of the inserted runs */ + + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_replace() invoked with NULL " + "pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* First, see if the left and right ends need merging. */ + if ((loc + 1) < dsize) + right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); + if (loc > 0) + left = ntfs_rl_are_mergeable(dst + loc - 1, src); + + /* Allocate some space. We'll need less if the left, right, or both + * ends get merged. The -1 accounts for the run being replaced. + */ + delta = ssize - 1 - left - right; + if (delta > 0) { + dst = ntfs_rl_realloc(dst, dsize, dsize + delta); + if (!dst) + return NULL; + } + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* First, merge the left and right ends, if necessary. */ + if (right) + __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); + if (left) + __ntfs_rl_merge(dst + loc - 1, src); + + /* + * tail - Offset of the tail of @dst + * Nominally: @tail = @loc + 1 (location, skipping the replaced run) + * If "right", then one of @dst's runs is already merged into @src. + */ + tail = loc + right + 1; + + /* + * marker - First run after the @src runs that have been inserted + * Nominally: @marker = @loc + @ssize (location + number of runs in @src) + * If "left", then the first run in @src has been merged with one in @dst. + */ + marker = loc + ssize - left; + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, marker, tail, dsize - tail); + ntfs_rl_mc(dst, loc, src, left, ssize - left); + + /* We may have changed the length of the file, so fix the end marker */ + if (((dsize - tail) > 0) && (dst[marker].lcn == LCN_ENOENT)) + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; + + return dst; +} + +/** + * ntfs_rl_split - insert a runlist into the centre of a hole + * @dst: original runlist to be worked on + * @dsize: number of elements in @dst (including end marker) + * @src: new runlist to be inserted + * @ssize: number of elements in @src (excluding end marker) + * @loc: index in runlist @dst at which to split and insert @src + * + * Split the runlist @dst at @loc into two and insert @new in between the two + * fragments. No merging of runlists is necessary. Adjust the size of the + * holes either side. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @dst and @src are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. + */ +static runlist_element *ntfs_rl_split(runlist_element *dst, int dsize, + runlist_element *src, int ssize, int loc) +{ + if (!dst || !src) { + ntfs_log_debug("Eeek. ntfs_rl_split() invoked with NULL pointer!\n"); + errno = EINVAL; + return NULL; + } + + /* Space required: @dst size + @src size + one new hole. */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize + 1); + if (!dst) + return dst; + /* + * We are guaranteed to succeed from here so can start modifying the + * original runlists. + */ + + /* Move the tail of @dst out of the way, then copy in @src. */ + ntfs_rl_mm(dst, loc + 1 + ssize, loc, dsize - loc); + ntfs_rl_mc(dst, loc + 1, src, 0, ssize); + + /* Adjust the size of the holes either size of @src. */ + dst[loc].length = dst[loc+1].vcn - dst[loc].vcn; + dst[loc+ssize+1].vcn = dst[loc+ssize].vcn + dst[loc+ssize].length; + dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn; + + return dst; +} + + +/** + * ntfs_runlists_merge_i - see ntfs_runlists_merge + */ +static runlist_element *ntfs_runlists_merge_i(runlist_element *drl, + runlist_element *srl) +{ + int di, si; /* Current index into @[ds]rl. */ + int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */ + int dins; /* Index into @drl at which to insert @srl. */ + int dend, send; /* Last index into @[ds]rl. */ + int dfinal, sfinal; /* The last index into @[ds]rl with + lcn >= LCN_HOLE. */ + int marker = 0; + VCN marker_vcn = 0; + + ntfs_log_debug("dst:\n"); + ntfs_debug_runlist_dump(drl); + ntfs_log_debug("src:\n"); + ntfs_debug_runlist_dump(srl); + + /* Check for silly calling... */ + if (!srl) + return drl; + + /* Check for the case where the first mapping is being done now. */ + if (!drl) { + drl = srl; + /* Complete the source runlist if necessary. */ + if (drl[0].vcn) { + /* Scan to the end of the source runlist. */ + for (dend = 0; drl[dend].length; dend++) + ; + dend++; + drl = ntfs_rl_realloc(drl, dend, dend + 1); + if (!drl) + return drl; + /* Insert start element at the front of the runlist. */ + ntfs_rl_mm(drl, 1, 0, dend); + drl[0].vcn = 0; + drl[0].lcn = LCN_RL_NOT_MAPPED; + drl[0].length = drl[1].vcn; + } + goto finished; + } + + si = di = 0; + + /* Skip any unmapped start element(s) in the source runlist. */ + while (srl[si].length && srl[si].lcn < (LCN)LCN_HOLE) + si++; + + /* Can't have an entirely unmapped source runlist. */ + if (!srl[si].length) { + errno = EINVAL; + ntfs_log_perror("%s: unmapped source runlist", __FUNCTION__); + return NULL; + } + + /* Record the starting points. */ + sstart = si; + + /* + * Skip forward in @drl until we reach the position where @srl needs to + * be inserted. If we reach the end of @drl, @srl just needs to be + * appended to @drl. + */ + for (; drl[di].length; di++) { + if (drl[di].vcn + drl[di].length > srl[sstart].vcn) + break; + } + dins = di; + + /* Sanity check for illegal overlaps. */ + if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) && + (srl[si].lcn >= 0)) { + errno = ERANGE; + ntfs_log_perror("Run lists overlap. Cannot merge"); + return NULL; + } + + /* Scan to the end of both runlists in order to know their sizes. */ + for (send = si; srl[send].length; send++) + ; + for (dend = di; drl[dend].length; dend++) + ; + + if (srl[send].lcn == (LCN)LCN_ENOENT) + marker_vcn = srl[marker = send].vcn; + + /* Scan to the last element with lcn >= LCN_HOLE. */ + for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--) + ; + for (dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal--) + ; + + { + BOOL start; + BOOL finish; + int ds = dend + 1; /* Number of elements in drl & srl */ + int ss = sfinal - sstart + 1; + + start = ((drl[dins].lcn < LCN_RL_NOT_MAPPED) || /* End of file */ + (drl[dins].vcn == srl[sstart].vcn)); /* Start of hole */ + finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) && /* End of file */ + ((drl[dins].vcn + drl[dins].length) <= /* End of hole */ + (srl[send - 1].vcn + srl[send - 1].length))); + + /* Or we'll lose an end marker */ + if (finish && !drl[dins].length) + ss++; + if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn)) + finish = FALSE; + + ntfs_log_debug("dfinal = %i, dend = %i\n", dfinal, dend); + ntfs_log_debug("sstart = %i, sfinal = %i, send = %i\n", sstart, sfinal, send); + ntfs_log_debug("start = %i, finish = %i\n", start, finish); + ntfs_log_debug("ds = %i, ss = %i, dins = %i\n", ds, ss, dins); + + if (start) { + if (finish) + drl = ntfs_rl_replace(drl, ds, srl + sstart, ss, dins); + else + drl = ntfs_rl_insert(drl, ds, srl + sstart, ss, dins); + } else { + if (finish) + drl = ntfs_rl_append(drl, ds, srl + sstart, ss, dins); + else + drl = ntfs_rl_split(drl, ds, srl + sstart, ss, dins); + } + if (!drl) { + ntfs_log_perror("Merge failed"); + return drl; + } + free(srl); + if (marker) { + ntfs_log_debug("Triggering marker code.\n"); + for (ds = dend; drl[ds].length; ds++) + ; + /* We only need to care if @srl ended after @drl. */ + if (drl[ds].vcn <= marker_vcn) { + int slots = 0; + + if (drl[ds].vcn == marker_vcn) { + ntfs_log_debug("Old marker = %lli, replacing with " + "LCN_ENOENT.\n", + (long long)drl[ds].lcn); + drl[ds].lcn = (LCN)LCN_ENOENT; + goto finished; + } + /* + * We need to create an unmapped runlist element in + * @drl or extend an existing one before adding the + * ENOENT terminator. + */ + if (drl[ds].lcn == (LCN)LCN_ENOENT) { + ds--; + slots = 1; + } + if (drl[ds].lcn != (LCN)LCN_RL_NOT_MAPPED) { + /* Add an unmapped runlist element. */ + if (!slots) { + /* FIXME/TODO: We need to have the + * extra memory already! (AIA) + */ + drl = ntfs_rl_realloc(drl, ds, ds + 2); + if (!drl) + goto critical_error; + slots = 2; + } + ds++; + /* Need to set vcn if it isn't set already. */ + if (slots != 1) + drl[ds].vcn = drl[ds - 1].vcn + + drl[ds - 1].length; + drl[ds].lcn = (LCN)LCN_RL_NOT_MAPPED; + /* We now used up a slot. */ + slots--; + } + drl[ds].length = marker_vcn - drl[ds].vcn; + /* Finally add the ENOENT terminator. */ + ds++; + if (!slots) { + /* FIXME/TODO: We need to have the extra + * memory already! (AIA) + */ + drl = ntfs_rl_realloc(drl, ds, ds + 1); + if (!drl) + goto critical_error; + } + drl[ds].vcn = marker_vcn; + drl[ds].lcn = (LCN)LCN_ENOENT; + drl[ds].length = (s64)0; + } + } + } + +finished: + /* The merge was completed successfully. */ + ntfs_log_debug("Merged runlist:\n"); + ntfs_debug_runlist_dump(drl); + return drl; + +critical_error: + /* Critical error! We cannot afford to fail here. */ + ntfs_log_perror("libntfs: Critical error"); + ntfs_log_debug("Forcing segmentation fault!\n"); + marker_vcn = ((runlist*)NULL)->lcn; + return drl; +} + +/** + * ntfs_runlists_merge - merge two runlists into one + * @drl: original runlist to be worked on + * @srl: new runlist to be merged into @drl + * + * First we sanity check the two runlists @srl and @drl to make sure that they + * are sensible and can be merged. The runlist @srl must be either after the + * runlist @drl or completely within a hole (or unmapped region) in @drl. + * + * Merging of runlists is necessary in two cases: + * 1. When attribute lists are used and a further extent is being mapped. + * 2. When new clusters are allocated to fill a hole or extend a file. + * + * There are four possible ways @srl can be merged. It can: + * - be inserted at the beginning of a hole, + * - split the hole in two and be inserted between the two fragments, + * - be appended at the end of a hole, or it can + * - replace the whole hole. + * It can also be appended to the end of the runlist, which is just a variant + * of the insert case. + * + * On success, return a pointer to the new, combined, runlist. Note, both + * runlists @drl and @srl are deallocated before returning so you cannot use + * the pointers for anything any more. (Strictly speaking the returned runlist + * may be the same as @dst but this is irrelevant.) + * + * On error, return NULL, with errno set to the error code. Both runlists are + * left unmodified. The following error codes are defined: + * ENOMEM Not enough memory to allocate runlist array. + * EINVAL Invalid parameters were passed in. + * ERANGE The runlists overlap and cannot be merged. + */ +runlist_element *ntfs_runlists_merge(runlist_element *drl, + runlist_element *srl) +{ + runlist_element *rl; + + ntfs_log_enter("Entering\n"); + rl = ntfs_runlists_merge_i(drl, srl); + ntfs_log_leave("\n"); + return rl; +} + +/** + * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist + * @vol: ntfs volume on which the attribute resides + * @attr: attribute record whose mapping pairs array to decompress + * @old_rl: optional runlist in which to insert @attr's runlist + * + * Decompress the attribute @attr's mapping pairs array into a runlist. On + * success, return the decompressed runlist. + * + * If @old_rl is not NULL, decompressed runlist is inserted into the + * appropriate place in @old_rl and the resultant, combined runlist is + * returned. The original @old_rl is deallocated. + * + * On error, return NULL with errno set to the error code. @old_rl is left + * unmodified in that case. + * + * The following error codes are defined: + * ENOMEM Not enough memory to allocate runlist array. + * EIO Corrupt runlist. + * EINVAL Invalid parameters were passed in. + * ERANGE The two runlists overlap. + * + * FIXME: For now we take the conceptionally simplest approach of creating the + * new runlist disregarding the already existing one and then splicing the + * two into one, if that is possible (we check for overlap and discard the new + * runlist if overlap present before returning NULL, with errno = ERANGE). + */ +static runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl) +{ + VCN vcn; /* Current vcn. */ + LCN lcn; /* Current lcn. */ + s64 deltaxcn; /* Change in [vl]cn. */ + runlist_element *rl; /* The output runlist. */ + const u8 *buf; /* Current position in mapping pairs array. */ + const u8 *attr_end; /* End of attribute. */ + int err, rlsize; /* Size of runlist buffer. */ + u16 rlpos; /* Current runlist position in units of + runlist_elements. */ + u8 b; /* Current byte offset in buf. */ + + ntfs_log_trace("Entering for attr 0x%x.\n", + (unsigned)le32_to_cpu(attr->type)); + /* Make sure attr exists and is non-resident. */ + if (!attr || !attr->non_resident || + sle64_to_cpu(attr->lowest_vcn) < (VCN)0) { + errno = EINVAL; + return NULL; + } + /* Start at vcn = lowest_vcn and lcn 0. */ + vcn = sle64_to_cpu(attr->lowest_vcn); + lcn = 0; + /* Get start of the mapping pairs array. */ + buf = (const u8*)attr + le16_to_cpu(attr->mapping_pairs_offset); + attr_end = (const u8*)attr + le32_to_cpu(attr->length); + if (buf < (const u8*)attr || buf > attr_end) { + ntfs_log_debug("Corrupt attribute.\n"); + errno = EIO; + return NULL; + } + /* Current position in runlist array. */ + rlpos = 0; + /* Allocate first 4kiB block and set current runlist size to 4kiB. */ + rlsize = 0x1000; + rl = ntfs_malloc(rlsize); + if (!rl) + return NULL; + /* Insert unmapped starting element if necessary. */ + if (vcn) { + rl->vcn = (VCN)0; + rl->lcn = (LCN)LCN_RL_NOT_MAPPED; + rl->length = vcn; + rlpos++; + } + while (buf < attr_end && *buf) { + /* + * Allocate more memory if needed, including space for the + * not-mapped and terminator elements. + */ + if ((int)((rlpos + 3) * sizeof(*old_rl)) > rlsize) { + runlist_element *rl2; + + rlsize += 0x1000; + rl2 = realloc(rl, rlsize); + if (!rl2) { + int eo = errno; + free(rl); + errno = eo; + return NULL; + } + rl = rl2; + } + /* Enter the current vcn into the current runlist element. */ + rl[rlpos].vcn = vcn; + /* + * Get the change in vcn, i.e. the run length in clusters. + * Doing it this way ensures that we signextend negative values. + * A negative run length doesn't make any sense, but hey, I + * didn't make up the NTFS specs and Windows NT4 treats the run + * length as a signed value so that's how it is... + */ + b = *buf & 0xf; + if (b) { + if (buf + b > attr_end) + goto io_error; + for (deltaxcn = (s8)buf[b--]; b; b--) + deltaxcn = (deltaxcn << 8) + buf[b]; + } else { /* The length entry is compulsory. */ + ntfs_log_debug("Missing length entry in mapping pairs " + "array.\n"); + deltaxcn = (s64)-1; + } + /* + * Assume a negative length to indicate data corruption and + * hence clean-up and return NULL. + */ + if (deltaxcn < 0) { + ntfs_log_debug("Invalid length in mapping pairs array.\n"); + goto err_out; + } + /* + * Enter the current run length into the current runlist + * element. + */ + rl[rlpos].length = deltaxcn; + /* Increment the current vcn by the current run length. */ + vcn += deltaxcn; + /* + * There might be no lcn change at all, as is the case for + * sparse clusters on NTFS 3.0+, in which case we set the lcn + * to LCN_HOLE. + */ + if (!(*buf & 0xf0)) + rl[rlpos].lcn = (LCN)LCN_HOLE; + else { + /* Get the lcn change which really can be negative. */ + u8 b2 = *buf & 0xf; + b = b2 + ((*buf >> 4) & 0xf); + if (buf + b > attr_end) + goto io_error; + for (deltaxcn = (s8)buf[b--]; b > b2; b--) + deltaxcn = (deltaxcn << 8) + buf[b]; + /* Change the current lcn to it's new value. */ + lcn += deltaxcn; +#ifdef DEBUG + /* + * On NTFS 1.2-, apparently can have lcn == -1 to + * indicate a hole. But we haven't verified ourselves + * whether it is really the lcn or the deltaxcn that is + * -1. So if either is found give us a message so we + * can investigate it further! + */ + if (vol->major_ver < 3) { + if (deltaxcn == (LCN)-1) + ntfs_log_debug("lcn delta == -1\n"); + if (lcn == (LCN)-1) + ntfs_log_debug("lcn == -1\n"); + } +#endif + /* Check lcn is not below -1. */ + if (lcn < (LCN)-1) { + ntfs_log_debug("Invalid LCN < -1 in mapping pairs " + "array.\n"); + goto err_out; + } + /* Enter the current lcn into the runlist element. */ + rl[rlpos].lcn = lcn; + } + /* Get to the next runlist element. */ + rlpos++; + /* Increment the buffer position to the next mapping pair. */ + buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1; + } + if (buf >= attr_end) + goto io_error; + /* + * If there is a highest_vcn specified, it must be equal to the final + * vcn in the runlist - 1, or something has gone badly wrong. + */ + deltaxcn = sle64_to_cpu(attr->highest_vcn); + if (deltaxcn && vcn - 1 != deltaxcn) { +mpa_err: + ntfs_log_debug("Corrupt mapping pairs array in non-resident " + "attribute.\n"); + goto err_out; + } + /* Setup not mapped runlist element if this is the base extent. */ + if (!attr->lowest_vcn) { + VCN max_cluster; + + max_cluster = ((sle64_to_cpu(attr->allocated_size) + + vol->cluster_size - 1) >> + vol->cluster_size_bits) - 1; + /* + * A highest_vcn of zero means this is a single extent + * attribute so simply terminate the runlist with LCN_ENOENT). + */ + if (deltaxcn) { + /* + * If there is a difference between the highest_vcn and + * the highest cluster, the runlist is either corrupt + * or, more likely, there are more extents following + * this one. + */ + if (deltaxcn < max_cluster) { + ntfs_log_debug("More extents to follow; deltaxcn = " + "0x%llx, max_cluster = 0x%llx\n", + (long long)deltaxcn, + (long long)max_cluster); + rl[rlpos].vcn = vcn; + vcn += rl[rlpos].length = max_cluster - deltaxcn; + rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; + rlpos++; + } else if (deltaxcn > max_cluster) { + ntfs_log_debug("Corrupt attribute. deltaxcn = " + "0x%llx, max_cluster = 0x%llx\n", + (long long)deltaxcn, + (long long)max_cluster); + goto mpa_err; + } + } + rl[rlpos].lcn = (LCN)LCN_ENOENT; + } else /* Not the base extent. There may be more extents to follow. */ + rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; + + /* Setup terminating runlist element. */ + rl[rlpos].vcn = vcn; + rl[rlpos].length = (s64)0; + /* If no existing runlist was specified, we are done. */ + if (!old_rl) { + ntfs_log_debug("Mapping pairs array successfully decompressed:\n"); + ntfs_debug_runlist_dump(rl); + return rl; + } + /* Now combine the new and old runlists checking for overlaps. */ + old_rl = ntfs_runlists_merge(old_rl, rl); + if (old_rl) + return old_rl; + err = errno; + free(rl); + ntfs_log_debug("Failed to merge runlists.\n"); + errno = err; + return NULL; +io_error: + ntfs_log_debug("Corrupt attribute.\n"); +err_out: + free(rl); + errno = EIO; + return NULL; +} + +runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, + const ATTR_RECORD *attr, runlist_element *old_rl) +{ + runlist_element *rle; + + ntfs_log_enter("Entering\n"); + rle = ntfs_mapping_pairs_decompress_i(vol, attr, old_rl); + ntfs_log_leave("\n"); + return rle; +} + +/** + * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist + * @rl: runlist to use for conversion + * @vcn: vcn to convert + * + * Convert the virtual cluster number @vcn of an attribute into a logical + * cluster number (lcn) of a device using the runlist @rl to map vcns to their + * corresponding lcns. + * + * Since lcns must be >= 0, we use negative return values with special meaning: + * + * Return value Meaning / Description + * ================================================== + * -1 = LCN_HOLE Hole / not allocated on disk. + * -2 = LCN_RL_NOT_MAPPED This is part of the runlist which has not been + * inserted into the runlist yet. + * -3 = LCN_ENOENT There is no such vcn in the attribute. + * -4 = LCN_EINVAL Input parameter error. + */ +LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn) +{ + int i; + + if (vcn < (VCN)0) + return (LCN)LCN_EINVAL; + /* + * If rl is NULL, assume that we have found an unmapped runlist. The + * caller can then attempt to map it and fail appropriately if + * necessary. + */ + if (!rl) + return (LCN)LCN_RL_NOT_MAPPED; + + /* Catch out of lower bounds vcn. */ + if (vcn < rl[0].vcn) + return (LCN)LCN_ENOENT; + + for (i = 0; rl[i].length; i++) { + if (vcn < rl[i+1].vcn) { + if (rl[i].lcn >= (LCN)0) + return rl[i].lcn + (vcn - rl[i].vcn); + return rl[i].lcn; + } + } + /* + * The terminator element is setup to the correct value, i.e. one of + * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT. + */ + if (rl[i].lcn < (LCN)0) + return rl[i].lcn; + /* Just in case... We could replace this with BUG() some day. */ + return (LCN)LCN_ENOENT; +} + +/** + * ntfs_rl_pread - gather read from disk + * @vol: ntfs volume to read from + * @rl: runlist specifying where to read the data from + * @pos: byte position within runlist @rl at which to begin the read + * @count: number of bytes to read + * @b: data buffer into which to read from disk + * + * This function will read @count bytes from the volume @vol to the data buffer + * @b gathering the data as specified by the runlist @rl. The read begins at + * offset @pos into the runlist @rl. + * + * On success, return the number of successfully read bytes. If this number is + * lower than @count this means that the read reached end of file or that an + * error was encountered during the read so that the read is partial. 0 means + * nothing was read (also return 0 when @count is 0). + * + * On error and nothing has been read, return -1 with errno set appropriately + * to the return code of ntfs_pread(), or to EINVAL in case of invalid + * arguments. + * + * NOTE: If we encounter EOF while reading we return EIO because we assume that + * the run list must point to valid locations within the ntfs volume. + */ +s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, + const s64 pos, s64 count, void *b) +{ + s64 bytes_read, to_read, ofs, total; + int err = EIO; + + if (!vol || !rl || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("Failed to read runlist [vol: %p rl: %p " + "pos: %lld count: %lld]", vol, rl, + (long long)pos, (long long)count); + return -1; + } + if (!count) + return count; + /* Seek in @rl to the run containing @pos. */ + for (ofs = 0; rl->length && (ofs + (rl->length << + vol->cluster_size_bits) <= pos); rl++) + ofs += (rl->length << vol->cluster_size_bits); + /* Offset in the run at which to begin reading. */ + ofs = pos - ofs; + for (total = 0LL; count; rl++, ofs = 0) { + if (!rl->length) + goto rl_err_out; + if (rl->lcn < (LCN)0) { + if (rl->lcn != (LCN)LCN_HOLE) + goto rl_err_out; + /* It is a hole. Just fill buffer @b with zeroes. */ + to_read = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + memset(b, 0, to_read); + /* Update counters and proceed with next run. */ + total += to_read; + count -= to_read; + b = (u8*)b + to_read; + continue; + } + /* It is a real lcn, read it from the volume. */ + to_read = min(count, (rl->length << vol->cluster_size_bits) - + ofs); +retry: + bytes_read = ntfs_pread(vol->dev, (rl->lcn << + vol->cluster_size_bits) + ofs, to_read, b); + /* If everything ok, update progress counters and continue. */ + if (bytes_read > 0) { + total += bytes_read; + count -= bytes_read; + b = (u8*)b + bytes_read; + continue; + } + /* If the syscall was interrupted, try again. */ + if (bytes_read == (s64)-1 && errno == EINTR) + goto retry; + if (bytes_read == (s64)-1) + err = errno; + goto rl_err_out; + } + /* Finally, return the number of bytes read. */ + return total; +rl_err_out: + if (total) + return total; + errno = err; + return -1; +} + +/** + * ntfs_rl_pwrite - scatter write to disk + * @vol: ntfs volume to write to + * @rl: runlist entry specifying where to write the data to + * @ofs: offset in file for runlist element indicated in @rl + * @pos: byte position from runlist beginning at which to begin the write + * @count: number of bytes to write + * @b: data buffer to write to disk + * + * This function will write @count bytes from data buffer @b to the volume @vol + * scattering the data as specified by the runlist @rl. The write begins at + * offset @pos into the runlist @rl. If a run is sparse then the related buffer + * data is ignored which means that the caller must ensure they are consistent. + * + * On success, return the number of successfully written bytes. If this number + * is lower than @count this means that the write has been interrupted in + * flight or that an error was encountered during the write so that the write + * is partial. 0 means nothing was written (also return 0 when @count is 0). + * + * On error and nothing has been written, return -1 with errno set + * appropriately to the return code of ntfs_pwrite(), or to to EINVAL in case + * of invalid arguments. + */ +s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, + s64 ofs, const s64 pos, s64 count, void *b) +{ + s64 written, to_write, total = 0; + int err = EIO; + + if (!vol || !rl || pos < 0 || count < 0) { + errno = EINVAL; + ntfs_log_perror("Failed to write runlist [vol: %p rl: %p " + "pos: %lld count: %lld]", vol, rl, + (long long)pos, (long long)count); + goto errno_set; + } + if (!count) + goto out; + /* Seek in @rl to the run containing @pos. */ + while (rl->length && (ofs + (rl->length << + vol->cluster_size_bits) <= pos)) { + ofs += (rl->length << vol->cluster_size_bits); + rl++; + } + /* Offset in the run at which to begin writing. */ + ofs = pos - ofs; + for (total = 0LL; count; rl++, ofs = 0) { + if (!rl->length) + goto rl_err_out; + if (rl->lcn < (LCN)0) { + + if (rl->lcn != (LCN)LCN_HOLE) + goto rl_err_out; + + to_write = min(count, (rl->length << + vol->cluster_size_bits) - ofs); + + total += to_write; + count -= to_write; + b = (u8*)b + to_write; + continue; + } + /* It is a real lcn, write it to the volume. */ + to_write = min(count, (rl->length << vol->cluster_size_bits) - + ofs); +retry: + if (!NVolReadOnly(vol)) + written = ntfs_pwrite(vol->dev, (rl->lcn << + vol->cluster_size_bits) + ofs, + to_write, b); + else + written = to_write; + /* If everything ok, update progress counters and continue. */ + if (written > 0) { + total += written; + count -= written; + b = (u8*)b + written; + continue; + } + /* If the syscall was interrupted, try again. */ + if (written == (s64)-1 && errno == EINTR) + goto retry; + if (written == (s64)-1) + err = errno; + goto rl_err_out; + } +out: + return total; +rl_err_out: + if (total) + goto out; + errno = err; +errno_set: + total = -1; + goto out; +} + +/** + * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number + * @n: number for which to get the number of bytes for + * + * Return the number of bytes required to store @n unambiguously as + * a signed number. + * + * This is used in the context of the mapping pairs array to determine how + * many bytes will be needed in the array to store a given logical cluster + * number (lcn) or a specific run length. + * + * Return the number of bytes written. This function cannot fail. + */ +int ntfs_get_nr_significant_bytes(const s64 n) +{ + u64 l; + int i; + + l = (n < 0 ? ~n : n); + i = 1; + if (l >= 128) { + l >>= 7; + do { + i++; + l >>= 8; + } while (l); + } + return i; +} + +/** + * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array + * @vol: ntfs volume (needed for the ntfs version) + * @rl: runlist for which to determine the size of the mapping pairs + * @start_vcn: vcn at which to start the mapping pairs array + * + * Walk the runlist @rl and calculate the size in bytes of the mapping pairs + * array corresponding to the runlist @rl, starting at vcn @start_vcn. This + * for example allows us to allocate a buffer of the right size when building + * the mapping pairs array. + * + * If @rl is NULL, just return 1 (for the single terminator byte). + * + * Return the calculated size in bytes on success. On error, return -1 with + * errno set to the error code. The following error codes are defined: + * EINVAL - Run list contains unmapped elements. Make sure to only pass + * fully mapped runlists to this function. + * - @start_vcn is invalid. + * EIO - The runlist is corrupt. + */ +int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, + const runlist_element *rl, const VCN start_vcn, int max_size) +{ + LCN prev_lcn; + int rls; + + if (start_vcn < 0) { + ntfs_log_trace("start_vcn %lld (should be >= 0)\n", + (long long) start_vcn); + errno = EINVAL; + goto errno_set; + } + if (!rl) { + if (start_vcn) { + ntfs_log_trace("rl NULL, start_vcn %lld (should be > 0)\n", + (long long) start_vcn); + errno = EINVAL; + goto errno_set; + } + rls = 1; + goto out; + } + /* Skip to runlist element containing @start_vcn. */ + while (rl->length && start_vcn >= rl[1].vcn) + rl++; + if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) { + errno = EINVAL; + goto errno_set; + } + prev_lcn = 0; + /* Always need the terminating zero byte. */ + rls = 1; + /* Do the first partial run if present. */ + if (start_vcn > rl->vcn) { + s64 delta; + + /* We know rl->length != 0 already. */ + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + delta = start_vcn - rl->vcn; + /* Header byte + length. */ + rls += 1 + ntfs_get_nr_significant_bytes(rl->length - delta); + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just store the lcn. + * Note: this assumes that on NTFS 1.2-, holes are stored with + * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + prev_lcn = rl->lcn; + if (rl->lcn >= 0) + prev_lcn += delta; + /* Change in lcn. */ + rls += ntfs_get_nr_significant_bytes(prev_lcn); + } + /* Go to next runlist element. */ + rl++; + } + /* Do the full runs. */ + for (; rl->length && (rls <= max_size); rl++) { + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + /* Header byte + length. */ + rls += 1 + ntfs_get_nr_significant_bytes(rl->length); + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just store the lcn. + * Note: this assumes that on NTFS 1.2-, holes are stored with + * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + /* Change in lcn. */ + rls += ntfs_get_nr_significant_bytes(rl->lcn - + prev_lcn); + prev_lcn = rl->lcn; + } + } +out: + return rls; +err_out: + if (rl->lcn == LCN_RL_NOT_MAPPED) + errno = EINVAL; + else + errno = EIO; +errno_set: + rls = -1; + goto out; +} + +/** + * ntfs_write_significant_bytes - write the significant bytes of a number + * @dst: destination buffer to write to + * @dst_max: pointer to last byte of destination buffer for bounds checking + * @n: number whose significant bytes to write + * + * Store in @dst, the minimum bytes of the number @n which are required to + * identify @n unambiguously as a signed number, taking care not to exceed + * @dest_max, the maximum position within @dst to which we are allowed to + * write. + * + * This is used when building the mapping pairs array of a runlist to compress + * a given logical cluster number (lcn) or a specific run length to the minimum + * size possible. + * + * Return the number of bytes written on success. On error, i.e. the + * destination buffer @dst is too small, return -1 with errno set ENOSPC. + */ +int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, const s64 n) +{ + s64 l = n; + int i; + + i = 0; + if (dst > dst_max) + goto err_out; + *dst++ = l; + i++; + while ((l > 0x7f) || (l < -0x80)) { + if (dst > dst_max) + goto err_out; + l >>= 8; + *dst++ = l; + i++; + } + return i; +err_out: + errno = ENOSPC; + return -1; +} + +/** + * ntfs_mapping_pairs_build - build the mapping pairs array from a runlist + * @vol: ntfs volume (needed for the ntfs version) + * @dst: destination buffer to which to write the mapping pairs array + * @dst_len: size of destination buffer @dst in bytes + * @rl: runlist for which to build the mapping pairs array + * @start_vcn: vcn at which to start the mapping pairs array + * @stop_vcn: first vcn outside destination buffer on success or ENOSPC error + * + * Create the mapping pairs array from the runlist @rl, starting at vcn + * @start_vcn and save the array in @dst. @dst_len is the size of @dst in + * bytes and it should be at least equal to the value obtained by calling + * ntfs_get_size_for_mapping_pairs(). + * + * If @rl is NULL, just write a single terminator byte to @dst. + * + * On success or ENOSPC error, if @stop_vcn is not NULL, *@stop_vcn is set to + * the first vcn outside the destination buffer. Note that on error @dst has + * been filled with all the mapping pairs that will fit, thus it can be treated + * as partial success, in that a new attribute extent needs to be created or the + * next extent has to be used and the mapping pairs build has to be continued + * with @start_vcn set to *@stop_vcn. + * + * Return 0 on success. On error, return -1 with errno set to the error code. + * The following error codes are defined: + * EINVAL - Run list contains unmapped elements. Make sure to only pass + * fully mapped runlists to this function. + * - @start_vcn is invalid. + * EIO - The runlist is corrupt. + * ENOSPC - The destination buffer is too small. + */ +int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, + const int dst_len, const runlist_element *rl, + const VCN start_vcn, runlist_element const **stop_rl) +{ + LCN prev_lcn; + u8 *dst_max, *dst_next; + s8 len_len, lcn_len; + int ret = 0; + + if (start_vcn < 0) + goto val_err; + if (!rl) { + if (start_vcn) + goto val_err; + if (stop_rl) + *stop_rl = rl; + if (dst_len < 1) + goto nospc_err; + goto ok; + } + /* Skip to runlist element containing @start_vcn. */ + while (rl->length && start_vcn >= rl[1].vcn) + rl++; + if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) + goto val_err; + /* + * @dst_max is used for bounds checking in + * ntfs_write_significant_bytes(). + */ + dst_max = dst + dst_len - 1; + prev_lcn = 0; + /* Do the first partial run if present. */ + if (start_vcn > rl->vcn) { + s64 delta; + + /* We know rl->length != 0 already. */ + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + delta = start_vcn - rl->vcn; + /* Write length. */ + len_len = ntfs_write_significant_bytes(dst + 1, dst_max, + rl->length - delta); + if (len_len < 0) + goto size_err; + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just write the lcn + * change. FIXME: Do we need to write the lcn change or just + * the lcn in that case? Not sure as I have never seen this + * case on NT4. - We assume that we just need to write the lcn + * change until someone tells us otherwise... (AIA) + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + prev_lcn = rl->lcn; + if (rl->lcn >= 0) + prev_lcn += delta; + /* Write change in lcn. */ + lcn_len = ntfs_write_significant_bytes(dst + 1 + + len_len, dst_max, prev_lcn); + if (lcn_len < 0) + goto size_err; + } else + lcn_len = 0; + dst_next = dst + len_len + lcn_len + 1; + if (dst_next > dst_max) + goto size_err; + /* Update header byte. */ + *dst = lcn_len << 4 | len_len; + /* Position at next mapping pairs array element. */ + dst = dst_next; + /* Go to next runlist element. */ + rl++; + } + /* Do the full runs. */ + for (; rl->length; rl++) { + if (rl->length < 0 || rl->lcn < LCN_HOLE) + goto err_out; + /* Write length. */ + len_len = ntfs_write_significant_bytes(dst + 1, dst_max, + rl->length); + if (len_len < 0) + goto size_err; + /* + * If the logical cluster number (lcn) denotes a hole and we + * are on NTFS 3.0+, we don't store it at all, i.e. we need + * zero space. On earlier NTFS versions we just write the lcn + * change. FIXME: Do we need to write the lcn change or just + * the lcn in that case? Not sure as I have never seen this + * case on NT4. - We assume that we just need to write the lcn + * change until someone tells us otherwise... (AIA) + */ + if (rl->lcn >= 0 || vol->major_ver < 3) { + /* Write change in lcn. */ + lcn_len = ntfs_write_significant_bytes(dst + 1 + + len_len, dst_max, rl->lcn - prev_lcn); + if (lcn_len < 0) + goto size_err; + prev_lcn = rl->lcn; + } else + lcn_len = 0; + dst_next = dst + len_len + lcn_len + 1; + if (dst_next > dst_max) + goto size_err; + /* Update header byte. */ + *dst = lcn_len << 4 | len_len; + /* Position at next mapping pairs array element. */ + dst += 1 + len_len + lcn_len; + } + /* Set stop vcn. */ + if (stop_rl) + *stop_rl = rl; +ok: + /* Add terminator byte. */ + *dst = 0; +out: + return ret; +size_err: + /* Set stop vcn. */ + if (stop_rl) + *stop_rl = rl; + /* Add terminator byte. */ + *dst = 0; +nospc_err: + errno = ENOSPC; + goto errno_set; +val_err: + errno = EINVAL; + goto errno_set; +err_out: + if (rl->lcn == LCN_RL_NOT_MAPPED) + errno = EINVAL; + else + errno = EIO; +errno_set: + ret = -1; + goto out; +} + +/** + * ntfs_rl_truncate - truncate a runlist starting at a specified vcn + * @arl: address of runlist to truncate + * @start_vcn: first vcn which should be cut off + * + * Truncate the runlist *@arl starting at vcn @start_vcn as well as the memory + * buffer holding the runlist. + * + * Return 0 on success and -1 on error with errno set to the error code. + * + * NOTE: @arl is the address of the runlist. We need the address so we can + * modify the pointer to the runlist with the new, reallocated memory buffer. + */ +int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) +{ + runlist *rl; + /* BOOL is_end = FALSE; */ + + if (!arl || !*arl) { + errno = EINVAL; + if (!arl) + ntfs_log_perror("rl_truncate error: arl: %p", arl); + else + ntfs_log_perror("rl_truncate error:" + " arl: %p *arl: %p", arl, *arl); + return -1; + } + + rl = *arl; + + if (start_vcn < rl->vcn) { + errno = EINVAL; + ntfs_log_perror("Start_vcn lies outside front of runlist"); + return -1; + } + + /* Find the starting vcn in the run list. */ + while (rl->length) { + if (start_vcn < rl[1].vcn) + break; + rl++; + } + + if (!rl->length) { + errno = EIO; + ntfs_log_trace("Truncating already truncated runlist?\n"); + return -1; + } + + /* Truncate the run. */ + rl->length = start_vcn - rl->vcn; + + /* + * If a run was partially truncated, make the following runlist + * element a terminator instead of the truncated runlist + * element itself. + */ + if (rl->length) { + ++rl; +/* + if (!rl->length) + is_end = TRUE; +*/ + rl->vcn = start_vcn; + rl->length = 0; + } + rl->lcn = (LCN)LCN_ENOENT; + /** + * Reallocate memory if necessary. + * FIXME: Below code is broken, because runlist allocations must be + * a multiple of 4096. The code caused crashes and corruptions. + */ +/* + if (!is_end) { + size_t new_size = (rl - *arl + 1) * sizeof(runlist_element); + rl = realloc(*arl, new_size); + if (rl) + *arl = rl; + } +*/ + return 0; +} + +/** + * ntfs_rl_sparse - check whether runlist have sparse regions or not. + * @rl: runlist to check + * + * Return 1 if have, 0 if not, -1 on error with errno set to the error code. + */ +int ntfs_rl_sparse(runlist *rl) +{ + runlist *rlc; + + if (!rl) { + errno = EINVAL; + ntfs_log_perror("%s: ", __FUNCTION__); + return -1; + } + + for (rlc = rl; rlc->length; rlc++) + if (rlc->lcn < 0) { + if (rlc->lcn != LCN_HOLE) { + errno = EINVAL; + ntfs_log_perror("%s: bad runlist", __FUNCTION__); + return -1; + } + return 1; + } + return 0; +} + +/** + * ntfs_rl_get_compressed_size - calculate length of non sparse regions + * @vol: ntfs volume (need for cluster size) + * @rl: runlist to calculate for + * + * Return compressed size or -1 on error with errno set to the error code. + */ +s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl) +{ + runlist *rlc; + s64 ret = 0; + + if (!rl) { + errno = EINVAL; + ntfs_log_perror("%s: ", __FUNCTION__); + return -1; + } + + for (rlc = rl; rlc->length; rlc++) { + if (rlc->lcn < 0) { + if (rlc->lcn != LCN_HOLE) { + errno = EINVAL; + ntfs_log_perror("%s: bad runlist", __FUNCTION__); + return -1; + } + } else + ret += rlc->length; + } + return ret << vol->cluster_size_bits; +} + + +#ifdef NTFS_TEST +/** + * test_rl_helper + */ +#define MKRL(R,V,L,S) \ + (R)->vcn = V; \ + (R)->lcn = L; \ + (R)->length = S; +/* +} +*/ +/** + * test_rl_dump_runlist - Runlist test: Display the contents of a runlist + * @rl: + * + * Description... + * + * Returns: + */ +static void test_rl_dump_runlist(const runlist_element *rl) +{ + int abbr = 0; /* abbreviate long lists */ + int len = 0; + int i; + const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "XXXX" }; + + if (!rl) { + printf(" Run list not present.\n"); + return; + } + + if (abbr) + for (len = 0; rl[len].length; len++) ; + + printf(" VCN LCN len\n"); + for (i = 0; ; i++, rl++) { + LCN lcn = rl->lcn; + + if ((abbr) && (len > 20)) { + if (i == 4) + printf(" ...\n"); + if ((i > 3) && (i < (len - 3))) + continue; + } + + if (lcn < (LCN)0) { + int ind = -lcn - 1; + + if (ind > -LCN_ENOENT - 1) + ind = 3; + printf("%8lld %8s %8lld\n", + rl->vcn, lcn_str[ind], rl->length); + } else + printf("%8lld %8lld %8lld\n", + rl->vcn, rl->lcn, rl->length); + if (!rl->length) + break; + } + if ((abbr) && (len > 20)) + printf(" (%d entries)\n", len+1); + printf("\n"); +} + +/** + * test_rl_runlists_merge - Runlist test: Merge two runlists + * @drl: + * @srl: + * + * Description... + * + * Returns: + */ +static runlist_element * test_rl_runlists_merge(runlist_element *drl, runlist_element *srl) +{ + runlist_element *res = NULL; + + printf("dst:\n"); + test_rl_dump_runlist(drl); + printf("src:\n"); + test_rl_dump_runlist(srl); + + res = ntfs_runlists_merge(drl, srl); + + printf("res:\n"); + test_rl_dump_runlist(res); + + return res; +} + +/** + * test_rl_read_buffer - Runlist test: Read a file containing a runlist + * @file: + * @buf: + * @bufsize: + * + * Description... + * + * Returns: + */ +static int test_rl_read_buffer(const char *file, u8 *buf, int bufsize) +{ + FILE *fptr; + + fptr = fopen(file, "r"); + if (!fptr) { + printf("open %s\n", file); + return 0; + } + + if (fread(buf, bufsize, 1, fptr) == 99) { + printf("read %s\n", file); + return 0; + } + + fclose(fptr); + return 1; +} + +/** + * test_rl_pure_src - Runlist test: Complicate the simple tests a little + * @contig: + * @multi: + * @vcn: + * @len: + * + * Description... + * + * Returns: + */ +static runlist_element * test_rl_pure_src(BOOL contig, BOOL multi, int vcn, int len) +{ + runlist_element *result; + int fudge; + + if (contig) + fudge = 0; + else + fudge = 999; + + result = ntfs_malloc(4096); + if (!result) + return NULL; + + if (multi) { + MKRL(result+0, vcn + (0*len/4), fudge + vcn + 1000 + (0*len/4), len / 4) + MKRL(result+1, vcn + (1*len/4), fudge + vcn + 1000 + (1*len/4), len / 4) + MKRL(result+2, vcn + (2*len/4), fudge + vcn + 1000 + (2*len/4), len / 4) + MKRL(result+3, vcn + (3*len/4), fudge + vcn + 1000 + (3*len/4), len / 4) + MKRL(result+4, vcn + (4*len/4), LCN_RL_NOT_MAPPED, 0) + } else { + MKRL(result+0, vcn, fudge + vcn + 1000, len) + MKRL(result+1, vcn + len, LCN_RL_NOT_MAPPED, 0) + } + return result; +} + +/** + * test_rl_pure_test - Runlist test: Perform tests using simple runlists + * @test: + * @contig: + * @multi: + * @vcn: + * @len: + * @file: + * @size: + * + * Description... + * + * Returns: + */ +static void test_rl_pure_test(int test, BOOL contig, BOOL multi, int vcn, int len, runlist_element *file, int size) +{ + runlist_element *src; + runlist_element *dst; + runlist_element *res; + + src = test_rl_pure_src(contig, multi, vcn, len); + dst = ntfs_malloc(4096); + if (!src || !dst) { + printf("Test %2d ---------- FAILED! (no free memory?)\n", test); + return; + } + + memcpy(dst, file, size); + + printf("Test %2d ----------\n", test); + res = test_rl_runlists_merge(dst, src); + + free(res); +} + +/** + * test_rl_pure - Runlist test: Create tests using simple runlists + * @contig: + * @multi: + * + * Description... + * + * Returns: + */ +static void test_rl_pure(char *contig, char *multi) +{ + /* VCN, LCN, len */ + static runlist_element file1[] = { + { 0, -1, 100 }, /* HOLE */ + { 100, 1100, 100 }, /* DATA */ + { 200, -1, 100 }, /* HOLE */ + { 300, 1300, 100 }, /* DATA */ + { 400, -1, 100 }, /* HOLE */ + { 500, -3, 0 } /* NOENT */ + }; + static runlist_element file2[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -1, 100 }, /* HOLE */ + { 200, -3, 0 } /* NOENT */ + }; + static runlist_element file3[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -3, 0 } /* NOENT */ + }; + static runlist_element file4[] = { + { 0, -3, 0 } /* NOENT */ + }; + static runlist_element file5[] = { + { 0, -2, 100 }, /* NOTMAP */ + { 100, 1100, 100 }, /* DATA */ + { 200, -2, 100 }, /* NOTMAP */ + { 300, 1300, 100 }, /* DATA */ + { 400, -2, 100 }, /* NOTMAP */ + { 500, -3, 0 } /* NOENT */ + }; + static runlist_element file6[] = { + { 0, 1000, 100 }, /* DATA */ + { 100, -2, 100 }, /* NOTMAP */ + { 200, -3, 0 } /* NOENT */ + }; + BOOL c, m; + + if (strcmp(contig, "contig") == 0) + c = TRUE; + else if (strcmp(contig, "noncontig") == 0) + c = FALSE; + else { + printf("rl pure [contig|noncontig] [single|multi]\n"); + return; + } + if (strcmp(multi, "multi") == 0) + m = TRUE; + else if (strcmp(multi, "single") == 0) + m = FALSE; + else { + printf("rl pure [contig|noncontig] [single|multi]\n"); + return; + } + + test_rl_pure_test(1, c, m, 0, 40, file1, sizeof(file1)); + test_rl_pure_test(2, c, m, 40, 40, file1, sizeof(file1)); + test_rl_pure_test(3, c, m, 60, 40, file1, sizeof(file1)); + test_rl_pure_test(4, c, m, 0, 100, file1, sizeof(file1)); + test_rl_pure_test(5, c, m, 200, 40, file1, sizeof(file1)); + test_rl_pure_test(6, c, m, 240, 40, file1, sizeof(file1)); + test_rl_pure_test(7, c, m, 260, 40, file1, sizeof(file1)); + test_rl_pure_test(8, c, m, 200, 100, file1, sizeof(file1)); + test_rl_pure_test(9, c, m, 400, 40, file1, sizeof(file1)); + test_rl_pure_test(10, c, m, 440, 40, file1, sizeof(file1)); + test_rl_pure_test(11, c, m, 460, 40, file1, sizeof(file1)); + test_rl_pure_test(12, c, m, 400, 100, file1, sizeof(file1)); + test_rl_pure_test(13, c, m, 160, 100, file2, sizeof(file2)); + test_rl_pure_test(14, c, m, 100, 140, file2, sizeof(file2)); + test_rl_pure_test(15, c, m, 200, 40, file2, sizeof(file2)); + test_rl_pure_test(16, c, m, 240, 40, file2, sizeof(file2)); + test_rl_pure_test(17, c, m, 100, 40, file3, sizeof(file3)); + test_rl_pure_test(18, c, m, 140, 40, file3, sizeof(file3)); + test_rl_pure_test(19, c, m, 0, 40, file4, sizeof(file4)); + test_rl_pure_test(20, c, m, 40, 40, file4, sizeof(file4)); + test_rl_pure_test(21, c, m, 0, 40, file5, sizeof(file5)); + test_rl_pure_test(22, c, m, 40, 40, file5, sizeof(file5)); + test_rl_pure_test(23, c, m, 60, 40, file5, sizeof(file5)); + test_rl_pure_test(24, c, m, 0, 100, file5, sizeof(file5)); + test_rl_pure_test(25, c, m, 200, 40, file5, sizeof(file5)); + test_rl_pure_test(26, c, m, 240, 40, file5, sizeof(file5)); + test_rl_pure_test(27, c, m, 260, 40, file5, sizeof(file5)); + test_rl_pure_test(28, c, m, 200, 100, file5, sizeof(file5)); + test_rl_pure_test(29, c, m, 400, 40, file5, sizeof(file5)); + test_rl_pure_test(30, c, m, 440, 40, file5, sizeof(file5)); + test_rl_pure_test(31, c, m, 460, 40, file5, sizeof(file5)); + test_rl_pure_test(32, c, m, 400, 100, file5, sizeof(file5)); + test_rl_pure_test(33, c, m, 160, 100, file6, sizeof(file6)); + test_rl_pure_test(34, c, m, 100, 140, file6, sizeof(file6)); +} + +/** + * test_rl_zero - Runlist test: Merge a zero-length runlist + * + * Description... + * + * Returns: + */ +static void test_rl_zero(void) +{ + runlist_element *jim = NULL; + runlist_element *bob = NULL; + + bob = calloc(3, sizeof(runlist_element)); + if (!bob) + return; + + MKRL(bob+0, 10, 99, 5) + MKRL(bob+1, 15, LCN_RL_NOT_MAPPED, 0) + + jim = test_rl_runlists_merge(jim, bob); + if (!jim) + return; + + free(jim); +} + +/** + * test_rl_frag_combine - Runlist test: Perform tests using fragmented files + * @vol: + * @attr1: + * @attr2: + * @attr3: + * + * Description... + * + * Returns: + */ +static void test_rl_frag_combine(ntfs_volume *vol, ATTR_RECORD *attr1, ATTR_RECORD *attr2, ATTR_RECORD *attr3) +{ + runlist_element *run1; + runlist_element *run2; + runlist_element *run3; + + run1 = ntfs_mapping_pairs_decompress(vol, attr1, NULL); + if (!run1) + return; + + run2 = ntfs_mapping_pairs_decompress(vol, attr2, NULL); + if (!run2) + return; + + run1 = test_rl_runlists_merge(run1, run2); + + run3 = ntfs_mapping_pairs_decompress(vol, attr3, NULL); + if (!run3) + return; + + run1 = test_rl_runlists_merge(run1, run3); + + free(run1); +} + +/** + * test_rl_frag - Runlist test: Create tests using very fragmented files + * @test: + * + * Description... + * + * Returns: + */ +static void test_rl_frag(char *test) +{ + ntfs_volume vol; + ATTR_RECORD *attr1 = ntfs_malloc(1024); + ATTR_RECORD *attr2 = ntfs_malloc(1024); + ATTR_RECORD *attr3 = ntfs_malloc(1024); + + if (!attr1 || !attr2 || !attr3) + goto out; + + vol.sb = NULL; + vol.sector_size_bits = 9; + vol.cluster_size = 2048; + vol.cluster_size_bits = 11; + vol.major_ver = 3; + + if (!test_rl_read_buffer("runlist-data/attr1.bin", (u8*) attr1, 1024)) + goto out; + if (!test_rl_read_buffer("runlist-data/attr2.bin", (u8*) attr2, 1024)) + goto out; + if (!test_rl_read_buffer("runlist-data/attr3.bin", (u8*) attr3, 1024)) + goto out; + + if (strcmp(test, "123") == 0) test_rl_frag_combine(&vol, attr1, attr2, attr3); + else if (strcmp(test, "132") == 0) test_rl_frag_combine(&vol, attr1, attr3, attr2); + else if (strcmp(test, "213") == 0) test_rl_frag_combine(&vol, attr2, attr1, attr3); + else if (strcmp(test, "231") == 0) test_rl_frag_combine(&vol, attr2, attr3, attr1); + else if (strcmp(test, "312") == 0) test_rl_frag_combine(&vol, attr3, attr1, attr2); + else if (strcmp(test, "321") == 0) test_rl_frag_combine(&vol, attr3, attr2, attr1); + else + printf("Frag: No such test '%s'\n", test); + +out: + free(attr1); + free(attr2); + free(attr3); +} + +/** + * test_rl_main - Runlist test: Program start (main) + * @argc: + * @argv: + * + * Description... + * + * Returns: + */ +int test_rl_main(int argc, char *argv[]) +{ + if ((argc == 2) && (strcmp(argv[1], "zero") == 0)) test_rl_zero(); + else if ((argc == 3) && (strcmp(argv[1], "frag") == 0)) test_rl_frag(argv[2]); + else if ((argc == 4) && (strcmp(argv[1], "pure") == 0)) test_rl_pure(argv[2], argv[3]); + else + printf("rl [zero|frag|pure] {args}\n"); + + return 0; +} + +#endif + diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c new file mode 100755 index 0000000000000000000000000000000000000000..e6d0587c79f32c3585fee213e9231aab981bfa97 --- /dev/null +++ b/libntfs-3g/security.c @@ -0,0 +1,5373 @@ +/** + * security.c - Handling security/ACLs in NTFS. Originated from the Linux-NTFS project. + * + * Copyright (c) 2004 Anton Altaparmakov + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2006 Yura Pakhuchiy + * Copyright (c) 2007-2014 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#include <unistd.h> +#include <pwd.h> +#include <grp.h> + +#include "compat.h" +#include "param.h" +#include "types.h" +#include "layout.h" +#include "attrib.h" +#include "index.h" +#include "dir.h" +#include "bitmap.h" +#include "security.h" +#include "acls.h" +#include "cache.h" +#include "misc.h" + +/* + * JPA NTFS constants or structs + * should be moved to layout.h + */ + +#define ALIGN_SDS_BLOCK 0x40000 /* Alignment for a $SDS block */ +#define ALIGN_SDS_ENTRY 16 /* Alignment for a $SDS entry */ +#define STUFFSZ 0x4000 /* unitary stuffing size for $SDS */ +#define FIRST_SECURITY_ID 0x100 /* Lowest security id */ + + /* Mask for attributes which can be forced */ +#define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY \ + | FILE_ATTR_HIDDEN \ + | FILE_ATTR_SYSTEM \ + | FILE_ATTR_ARCHIVE \ + | FILE_ATTR_TEMPORARY \ + | FILE_ATTR_OFFLINE \ + | FILE_ATTR_NOT_CONTENT_INDEXED ) + +struct SII { /* this is an image of an $SII index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keysecurid; + + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; /* documented as badly aligned */ + le32 dataoffsh; + le32 datasize; +} ; + +struct SDH { /* this is an image of an $SDH index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keyhash; + le32 keysecurid; + + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; + le32 dataoffsh; + le32 datasize; + le32 fill3; + } ; + +/* + * A few useful constants + */ + +static ntfschar sii_stream[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), + const_cpu_to_le16('I'), + const_cpu_to_le16('I'), + const_cpu_to_le16(0) }; +static ntfschar sdh_stream[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), + const_cpu_to_le16('D'), + const_cpu_to_le16('H'), + const_cpu_to_le16(0) }; + +/* + * null SID (S-1-0-0) + */ + +extern const SID *nullsid; + +/* + * The zero GUID. + */ + +static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0), + const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } }; +static const GUID *const zero_guid = &__zero_guid; + +/** + * ntfs_guid_is_zero - check if a GUID is zero + * @guid: [IN] guid to check + * + * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID + * and FALSE otherwise. + */ +BOOL ntfs_guid_is_zero(const GUID *guid) +{ + return (memcmp(guid, zero_guid, sizeof(*zero_guid))); +} + +/** + * ntfs_guid_to_mbs - convert a GUID to a multi byte string + * @guid: [IN] guid to convert + * @guid_str: [OUT] string in which to return the GUID (optional) + * + * Convert the GUID pointed to by @guid to a multi byte string of the form + * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". Therefore, @guid_str (if not NULL) + * needs to be able to store at least 37 bytes. + * + * If @guid_str is not NULL it will contain the converted GUID on return. If + * it is NULL a string will be allocated and this will be returned. The caller + * is responsible for free()ing the string in that case. + * + * On success return the converted string and on failure return NULL with errno + * set to the error code. + */ +char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str) +{ + char *_guid_str; + int res; + + if (!guid) { + errno = EINVAL; + return NULL; + } + _guid_str = guid_str; + if (!_guid_str) { + _guid_str = (char*)ntfs_malloc(37); + if (!_guid_str) + return _guid_str; + } + res = snprintf(_guid_str, 37, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + (unsigned int)le32_to_cpu(guid->data1), + le16_to_cpu(guid->data2), le16_to_cpu(guid->data3), + guid->data4[0], guid->data4[1], + guid->data4[2], guid->data4[3], guid->data4[4], + guid->data4[5], guid->data4[6], guid->data4[7]); + if (res == 36) + return _guid_str; + if (!guid_str) + free(_guid_str); + errno = EINVAL; + return NULL; +} + +/** + * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID + * @sid: [IN] SID for which to determine the maximum string size + * + * Determine the maximum multi byte string size in bytes which is needed to + * store the standard textual representation of the SID pointed to by @sid. + * See ntfs_sid_to_mbs(), below. + * + * On success return the maximum number of bytes needed to store the multi byte + * string and on failure return -1 with errno set to the error code. + */ +int ntfs_sid_to_mbs_size(const SID *sid) +{ + int size, i; + + if (!ntfs_sid_is_valid(sid)) { + errno = EINVAL; + return -1; + } + /* Start with "S-". */ + size = 2; + /* + * Add the SID_REVISION. Hopefully the compiler will optimize this + * away as SID_REVISION is a constant. + */ + for (i = SID_REVISION; i > 0; i /= 10) + size++; + /* Add the "-". */ + size++; + /* + * Add the identifier authority. If it needs to be in decimal, the + * maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be + * in hexadecimal, then maximum is 0x665544332211 = 14 characters. + */ + if (!sid->identifier_authority.high_part) + size += 10; + else + size += 14; + /* + * Finally, add the sub authorities. For each we have a "-" followed + * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters. + */ + size += (1 + 10) * sid->sub_authority_count; + /* We need the zero byte at the end, too. */ + size++; + return size * sizeof(char); +} + +/** + * ntfs_sid_to_mbs - convert a SID to a multi byte string + * @sid: [IN] SID to convert + * @sid_str: [OUT] string in which to return the SID (optional) + * @sid_str_size: [IN] size in bytes of @sid_str + * + * Convert the SID pointed to by @sid to its standard textual representation. + * @sid_str (if not NULL) needs to be able to store at least + * ntfs_sid_to_mbs_size() bytes. @sid_str_size is the size in bytes of + * @sid_str if @sid_str is not NULL. + * + * The standard textual representation of the SID is of the form: + * S-R-I-S-S... + * Where: + * - The first "S" is the literal character 'S' identifying the following + * digits as a SID. + * - R is the revision level of the SID expressed as a sequence of digits + * in decimal. + * - I is the 48-bit identifier_authority, expressed as digits in decimal, + * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. + * - S... is one or more sub_authority values, expressed as digits in + * decimal. + * + * If @sid_str is not NULL it will contain the converted SUID on return. If it + * is NULL a string will be allocated and this will be returned. The caller is + * responsible for free()ing the string in that case. + * + * On success return the converted string and on failure return NULL with errno + * set to the error code. + */ +char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size) +{ + u64 u; + le32 leauth; + char *s; + int i, j, cnt; + + /* + * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will + * check @sid, too. 8 is the minimum SID string size. + */ + if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) { + errno = EINVAL; + return NULL; + } + /* Allocate string if not provided. */ + if (!sid_str) { + cnt = ntfs_sid_to_mbs_size(sid); + if (cnt < 0) + return NULL; + s = (char*)ntfs_malloc(cnt); + if (!s) + return s; + sid_str = s; + /* So we know we allocated it. */ + sid_str_size = 0; + } else { + s = sid_str; + cnt = sid_str_size; + } + /* Start with "S-R-". */ + i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + /* Add the identifier authority. */ + for (u = i = 0, j = 40; i < 6; i++, j -= 8) + u += (u64)sid->identifier_authority.value[i] << j; + if (!sid->identifier_authority.high_part) + i = snprintf(s, cnt, "%lu", (unsigned long)u); + else + i = snprintf(s, cnt, "0x%llx", (unsigned long long)u); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + /* Finally, add the sub authorities. */ + for (j = 0; j < sid->sub_authority_count; j++) { + leauth = sid->sub_authority[j]; + i = snprintf(s, cnt, "-%u", (unsigned int) + le32_to_cpu(leauth)); + if (i < 0 || i >= cnt) + goto err_out; + s += i; + cnt -= i; + } + return sid_str; +err_out: + if (i >= cnt) + i = EMSGSIZE; + else + i = errno; + if (!sid_str_size) + free(sid_str); + errno = i; + return NULL; +} + +/** + * ntfs_generate_guid - generatates a random current guid. + * @guid: [OUT] pointer to a GUID struct to hold the generated guid. + * + * perhaps not a very good random number generator though... + */ +void ntfs_generate_guid(GUID *guid) +{ + unsigned int i; + u8 *p = (u8 *)guid; + + /* this is called at most once from mkntfs */ + srandom(time((time_t*)NULL) ^ (getpid() << 16)); + for (i = 0; i < sizeof(GUID); i++) { + p[i] = (u8)(random() & 0xFF); + if (i == 7) + p[7] = (p[7] & 0x0F) | 0x40; + if (i == 8) + p[8] = (p[8] & 0x3F) | 0x80; + } +} + +/** + * ntfs_security_hash - calculate the hash of a security descriptor + * @sd: self-relative security descriptor whose hash to calculate + * @length: size in bytes of the security descritor @sd + * + * Calculate the hash of the self-relative security descriptor @sd of length + * @length bytes. + * + * This hash is used in the $Secure system file as the primary key for the $SDH + * index and is also stored in the header of each security descriptor in the + * $SDS data stream as well as in the index data of both the $SII and $SDH + * indexes. In all three cases it forms part of the SDS_ENTRY_HEADER + * structure. + * + * Return the calculated security hash in little endian. + */ +le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len) +{ + const le32 *pos = (const le32*)sd; + const le32 *end = pos + (len >> 2); + u32 hash = 0; + + while (pos < end) { + hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3); + pos++; + } + return cpu_to_le32(hash); +} + +/* + * Get the first entry of current index block + * cut and pasted form ntfs_ie_get_first() in index.c + */ + +static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih) +{ + return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset)); +} + +/* + * Stuff a 256KB block into $SDS before writing descriptors + * into the block. + * + * This prevents $SDS from being automatically declared as sparse + * when the second copy of the first security descriptor is written + * 256KB further ahead. + * + * Having $SDS declared as a sparse file is not wrong by itself + * and chkdsk leaves it as a sparse file. It does however complain + * and add a sparse flag (0x0200) into field file_attributes of + * STANDARD_INFORMATION of $Secure. This probably means that a + * sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse + * files (FILE_ATTR_SPARSE_FILE). + * + * Windows normally does not convert to sparse attribute or sparse + * file. Stuffing is just a way to get to the same result. + */ + +static int entersecurity_stuff(ntfs_volume *vol, off_t offs) +{ + int res; + int written; + unsigned long total; + char *stuff; + + res = 0; + total = 0; + stuff = (char*)ntfs_malloc(STUFFSZ); + if (stuff) { + memset(stuff, 0, STUFFSZ); + do { + written = ntfs_attr_data_write(vol->secure_ni, + STREAM_SDS, 4, stuff, STUFFSZ, offs); + if (written == STUFFSZ) { + total += STUFFSZ; + offs += STUFFSZ; + } else { + errno = ENOSPC; + res = -1; + } + } while (!res && (total < ALIGN_SDS_BLOCK)); + free(stuff); + } else { + errno = ENOMEM; + res = -1; + } + return (res); +} + +/* + * Enter a new security descriptor into $Secure (data only) + * it has to be written twice with an offset of 256KB + * + * Should only be called by entersecurityattr() to ensure consistency + * + * Returns zero if sucessful + */ + +static int entersecurity_data(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, + le32 hash, le32 keyid, off_t offs, int gap) +{ + int res; + int written1; + int written2; + char *fullattr; + int fullsz; + SECURITY_DESCRIPTOR_HEADER *phsds; + + res = -1; + fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER); + fullattr = (char*)ntfs_malloc(fullsz); + if (fullattr) { + /* + * Clear the gap from previous descriptor + * this could be useful for appending the second + * copy to the end of file. When creating a new + * 256K block, the gap is cleared while writing + * the first copy + */ + if (gap) + memset(fullattr,0,gap); + memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)], + attr,attrsz); + phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap]; + phsds->hash = hash; + phsds->security_id = keyid; + phsds->offset = cpu_to_le64(offs); + phsds->length = cpu_to_le32(fullsz - gap); + written1 = ntfs_attr_data_write(vol->secure_ni, + STREAM_SDS, 4, fullattr, fullsz, + offs - gap); + written2 = ntfs_attr_data_write(vol->secure_ni, + STREAM_SDS, 4, fullattr, fullsz, + offs - gap + ALIGN_SDS_BLOCK); + if ((written1 == fullsz) + && (written2 == written1)) { + /* + * Make sure the data size for $SDS marks the end + * of the last security attribute. Windows uses + * this to determine where the next attribute will + * be written, which causes issues if chkdsk had + * previously deleted the last entries without + * adjusting the size. + */ + res = ntfs_attr_shrink_size(vol->secure_ni,STREAM_SDS, + 4, offs - gap + ALIGN_SDS_BLOCK + fullsz); + } + else + errno = ENOSPC; + free(fullattr); + } else + errno = ENOMEM; + return (res); +} + +/* + * Enter a new security descriptor in $Secure (indexes only) + * + * Should only be called by entersecurityattr() to ensure consistency + * + * Returns zero if sucessful + */ + +static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz, + le32 hash, le32 keyid, off_t offs) +{ + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + int res; + ntfs_index_context *xsii; + ntfs_index_context *xsdh; + struct SII newsii; + struct SDH newsdh; + + res = -1; + /* enter a new $SII record */ + + xsii = vol->secure_xsii; + ntfs_index_ctx_reinit(xsii); + newsii.offs = const_cpu_to_le16(20); + newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20); + newsii.fill1 = const_cpu_to_le32(0); + newsii.indexsz = const_cpu_to_le16(sizeof(struct SII)); + newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY)); + newsii.flags = const_cpu_to_le16(0); + newsii.fill2 = const_cpu_to_le16(0); + newsii.keysecurid = keyid; + newsii.hash = hash; + newsii.securid = keyid; + realign.all = cpu_to_le64(offs); + newsii.dataoffsh = realign.parts.dataoffsh; + newsii.dataoffsl = realign.parts.dataoffsl; + newsii.datasize = cpu_to_le32(attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) { + + /* enter a new $SDH record */ + + xsdh = vol->secure_xsdh; + ntfs_index_ctx_reinit(xsdh); + newsdh.offs = const_cpu_to_le16(24); + newsdh.size = const_cpu_to_le16( + sizeof(SECURITY_DESCRIPTOR_HEADER)); + newsdh.fill1 = const_cpu_to_le32(0); + newsdh.indexsz = const_cpu_to_le16( + sizeof(struct SDH)); + newsdh.indexksz = const_cpu_to_le16( + sizeof(SDH_INDEX_KEY)); + newsdh.flags = const_cpu_to_le16(0); + newsdh.fill2 = const_cpu_to_le16(0); + newsdh.keyhash = hash; + newsdh.keysecurid = keyid; + newsdh.hash = hash; + newsdh.securid = keyid; + newsdh.dataoffsh = realign.parts.dataoffsh; + newsdh.dataoffsl = realign.parts.dataoffsl; + newsdh.datasize = cpu_to_le32(attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + /* special filler value, Windows generally */ + /* fills with 0x00490049, sometimes with zero */ + newsdh.fill3 = const_cpu_to_le32(0x00490049); + if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh)) + res = 0; + } + return (res); +} + +/* + * Enter a new security descriptor in $Secure (data and indexes) + * Returns id of entry, or zero if there is a problem. + * (should not be called for NTFS version < 3.0) + * + * important : calls have to be serialized, however no locking is + * needed while fuse is not multithreaded + */ + +static le32 entersecurityattr(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, + le32 hash) +{ + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + le32 securid; + le32 keyid; + u32 newkey; + off_t offs; + int gap; + int size; + BOOL found; + struct SII *psii; + INDEX_ENTRY *entry; + INDEX_ENTRY *next; + ntfs_index_context *xsii; + int retries; + ntfs_attr *na; + int olderrno; + + /* find the first available securid beyond the last key */ + /* in $Secure:$SII. This also determines the first */ + /* available location in $Secure:$SDS, as this stream */ + /* is always appended to and the id's are allocated */ + /* in sequence */ + + securid = const_cpu_to_le32(0); + xsii = vol->secure_xsii; + ntfs_index_ctx_reinit(xsii); + offs = size = 0; + keyid = const_cpu_to_le32(-1); + olderrno = errno; + found = !ntfs_index_lookup((char*)&keyid, + sizeof(SII_INDEX_KEY), xsii); + if (!found && (errno != ENOENT)) { + ntfs_log_perror("Inconsistency in index $SII"); + psii = (struct SII*)NULL; + } else { + /* restore errno to avoid misinterpretation */ + errno = olderrno; + entry = xsii->entry; + psii = (struct SII*)xsii->entry; + } + if (psii) { + /* + * Get last entry in block, but must get first one + * one first, as we should already be beyond the + * last one. For some reason the search for the last + * entry sometimes does not return the last block... + * we assume this can only happen in root block + */ + if (xsii->is_in_root) + entry = ntfs_ie_get_first + ((INDEX_HEADER*)&xsii->ir->index); + else + entry = ntfs_ie_get_first + ((INDEX_HEADER*)&xsii->ib->index); + /* + * All index blocks should be at least half full + * so there always is a last entry but one, + * except when creating the first entry in index root. + * This was however found not to be true : chkdsk + * sometimes deletes all the (unused) keys in the last + * index block without rebalancing the tree. + * When this happens, a new search is restarted from + * the smallest key. + */ + keyid = const_cpu_to_le32(0); + retries = 0; + while (entry) { + next = ntfs_index_next(entry,xsii); + if (next) { + psii = (struct SII*)next; + /* save last key and */ + /* available position */ + keyid = psii->keysecurid; + realign.parts.dataoffsh + = psii->dataoffsh; + realign.parts.dataoffsl + = psii->dataoffsl; + offs = le64_to_cpu(realign.all); + size = le32_to_cpu(psii->datasize); + } + entry = next; + if (!entry && !keyid && !retries) { + /* search failed, retry from smallest key */ + ntfs_index_ctx_reinit(xsii); + found = !ntfs_index_lookup((char*)&keyid, + sizeof(SII_INDEX_KEY), xsii); + if (!found && (errno != ENOENT)) { + ntfs_log_perror("Index $SII is broken"); + psii = (struct SII*)NULL; + } else { + /* restore errno */ + errno = olderrno; + entry = xsii->entry; + psii = (struct SII*)entry; + } + if (psii + && !(psii->flags & INDEX_ENTRY_END)) { + /* save first key and */ + /* available position */ + keyid = psii->keysecurid; + realign.parts.dataoffsh + = psii->dataoffsh; + realign.parts.dataoffsl + = psii->dataoffsl; + offs = le64_to_cpu(realign.all); + size = le32_to_cpu(psii->datasize); + } + retries++; + } + } + } + if (!keyid) { + /* + * could not find any entry, before creating the first + * entry, make a double check by making sure size of $SII + * is less than needed for one entry + */ + securid = const_cpu_to_le32(0); + na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4); + if (na) { + if ((size_t)na->data_size < (sizeof(struct SII) + + sizeof(INDEX_ENTRY_HEADER))) { + ntfs_log_error("Creating the first security_id\n"); + securid = const_cpu_to_le32(FIRST_SECURITY_ID); + } + ntfs_attr_close(na); + } + if (!securid) { + ntfs_log_error("Error creating a security_id\n"); + errno = EIO; + } + } else { + newkey = le32_to_cpu(keyid) + 1; + securid = cpu_to_le32(newkey); + } + /* + * The security attr has to be written twice 256KB + * apart. This implies that offsets like + * 0x40000*odd_integer must be left available for + * the second copy. So align to next block when + * the last byte overflows on a wrong block. + */ + + if (securid) { + gap = (-size) & (ALIGN_SDS_ENTRY - 1); + offs += gap + size; + if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1) + & ALIGN_SDS_BLOCK) { + offs = ((offs + attrsz + + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1) + | (ALIGN_SDS_BLOCK - 1)) + 1; + } + if (!(offs & (ALIGN_SDS_BLOCK - 1))) + entersecurity_stuff(vol, offs); + /* + * now write the security attr to storage : + * first data, then SII, then SDH + * If failure occurs while writing SDS, data will never + * be accessed through indexes, and will be overwritten + * by the next allocated descriptor + * If failure occurs while writing SII, the id has not + * recorded and will be reallocated later + * If failure occurs while writing SDH, the space allocated + * in SDS or SII will not be reused, an inconsistency + * will persist with no significant consequence + */ + if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap) + || entersecurity_indexes(vol, attrsz, hash, securid, offs)) + securid = const_cpu_to_le32(0); + } + /* inode now is dirty, synchronize it all */ + ntfs_index_entry_mark_dirty(vol->secure_xsii); + ntfs_index_ctx_reinit(vol->secure_xsii); + ntfs_index_entry_mark_dirty(vol->secure_xsdh); + ntfs_index_ctx_reinit(vol->secure_xsdh); + NInoSetDirty(vol->secure_ni); + if (ntfs_inode_sync(vol->secure_ni)) + ntfs_log_perror("Could not sync $Secure\n"); + return (securid); +} + +/* + * Find a matching security descriptor in $Secure, + * if none, allocate a new id and write the descriptor to storage + * Returns id of entry, or zero if there is a problem. + * + * important : calls have to be serialized, however no locking is + * needed while fuse is not multithreaded + */ + +static le32 setsecurityattr(ntfs_volume *vol, + const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz) +{ + struct SDH *psdh; /* this is an image of index (le) */ + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + BOOL found; + BOOL collision; + size_t size; + size_t rdsize; + s64 offs; + int res; + ntfs_index_context *xsdh; + char *oldattr; + SDH_INDEX_KEY key; + INDEX_ENTRY *entry; + le32 securid; + le32 hash; + int olderrno; + + hash = ntfs_security_hash(attr,attrsz); + oldattr = (char*)NULL; + securid = const_cpu_to_le32(0); + res = 0; + xsdh = vol->secure_xsdh; + if (vol->secure_ni && xsdh && !vol->secure_reentry++) { + ntfs_index_ctx_reinit(xsdh); + /* + * find the nearest key as (hash,0) + * (do not search for partial key : in case of collision, + * it could return a key which is not the first one which + * collides) + */ + key.hash = hash; + key.security_id = const_cpu_to_le32(0); + olderrno = errno; + found = !ntfs_index_lookup((char*)&key, + sizeof(SDH_INDEX_KEY), xsdh); + if (!found && (errno != ENOENT)) + ntfs_log_perror("Inconsistency in index $SDH"); + else { + /* restore errno to avoid misinterpretation */ + errno = olderrno; + entry = xsdh->entry; + found = FALSE; + /* + * lookup() may return a node with no data, + * if so get next + */ + if (entry->ie_flags & INDEX_ENTRY_END) + entry = ntfs_index_next(entry,xsdh); + do { + collision = FALSE; + psdh = (struct SDH*)entry; + if (psdh) + size = (size_t) le32_to_cpu(psdh->datasize) + - sizeof(SECURITY_DESCRIPTOR_HEADER); + else size = 0; + /* if hash is not the same, the key is not present */ + if (psdh && (size > 0) + && (psdh->keyhash == hash)) { + /* if hash is the same */ + /* check the whole record */ + realign.parts.dataoffsh = psdh->dataoffsh; + realign.parts.dataoffsl = psdh->dataoffsl; + offs = le64_to_cpu(realign.all) + + sizeof(SECURITY_DESCRIPTOR_HEADER); + oldattr = (char*)ntfs_malloc(size); + if (oldattr) { + rdsize = ntfs_attr_data_read( + vol->secure_ni, + STREAM_SDS, 4, + oldattr, size, offs); + found = (rdsize == size) + && !memcmp(oldattr,attr,size); + free(oldattr); + /* if the records do not compare */ + /* (hash collision), try next one */ + if (!found) { + entry = ntfs_index_next( + entry,xsdh); + collision = TRUE; + } + } else + res = ENOMEM; + } + } while (collision && entry); + if (found) + securid = psdh->keysecurid; + else { + if (res) { + errno = res; + securid = const_cpu_to_le32(0); + } else { + /* + * no matching key : + * have to build a new one + */ + securid = entersecurityattr(vol, + attr, attrsz, hash); + } + } + } + } + if (--vol->secure_reentry) + ntfs_log_perror("Reentry error, check no multithreading\n"); + return (securid); +} + + +/* + * Update the security descriptor of a file + * Either as an attribute (complying with pre v3.x NTFS version) + * or, when possible, as an entry in $Secure (for NTFS v3.x) + * + * returns 0 if success + */ + +static int update_secur_descr(ntfs_volume *vol, + char *newattr, ntfs_inode *ni) +{ + int newattrsz; + int written; + int res; + ntfs_attr *na; + + newattrsz = ntfs_attr_size(newattr); + +#if !FORCE_FORMAT_v1x + if ((vol->major_ver < 3) || !vol->secure_ni) { +#endif + + /* update for NTFS format v1.x */ + + /* update the old security attribute */ + na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); + if (na) { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64) newattrsz); + /* overwrite value */ + if (!res) { + written = (int)ntfs_attr_pwrite(na, (s64) 0, + (s64) newattrsz, newattr); + if (written != newattrsz) { + ntfs_log_error("Failed to update " + "a v1.x security descriptor\n"); + errno = EIO; + res = -1; + } + } + + ntfs_attr_close(na); + /* if old security attribute was found, also */ + /* truncate standard information attribute to v1.x */ + /* this is needed when security data is wanted */ + /* as v1.x though volume is formatted for v3.x */ + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + clear_nino_flag(ni, v3_Extensions); + /* + * Truncating the record does not sweep extensions + * from copy in memory. Clear security_id to be safe + */ + ni->security_id = const_cpu_to_le32(0); + res = ntfs_attr_truncate(na, (s64)48); + ntfs_attr_close(na); + clear_nino_flag(ni, v3_Extensions); + } + } else { + /* + * insert the new security attribute if there + * were none + */ + res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0, (u8*)newattr, + (s64) newattrsz); + } +#if !FORCE_FORMAT_v1x + } else { + + /* update for NTFS format v3.x */ + + le32 securid; + + securid = setsecurityattr(vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + (s64)newattrsz); + if (securid) { + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + res = 0; + if (!test_nino_flag(ni, v3_Extensions)) { + /* expand standard information attribute to v3.x */ + res = ntfs_attr_truncate(na, + (s64)sizeof(STANDARD_INFORMATION)); + ni->owner_id = const_cpu_to_le32(0); + ni->quota_charged = const_cpu_to_le64(0); + ni->usn = const_cpu_to_le64(0); + ntfs_attr_remove(ni, + AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0); + } + set_nino_flag(ni, v3_Extensions); + ni->security_id = securid; + ntfs_attr_close(na); + } else { + ntfs_log_error("Failed to update " + "standard informations\n"); + errno = EIO; + res = -1; + } + } else + res = -1; + } +#endif + + /* mark node as dirty */ + NInoSetDirty(ni); + return (res); +} + +/* + * Upgrade the security descriptor of a file + * This is intended to allow graceful upgrades for files which + * were created in previous versions, with a security attributes + * and no security id. + * + * It will allocate a security id and replace the individual + * security attribute by a reference to the global one + * + * Special files are not upgraded (currently / and files in + * directories /$*) + * + * Though most code is similar to update_secur_desc() it has + * been kept apart to facilitate the further processing of + * special cases or even to remove it if found dangerous. + * + * returns 0 if success, + * 1 if not upgradable. This is not an error. + * -1 if there is a problem + */ + +static int upgrade_secur_desc(ntfs_volume *vol, + const char *attr, ntfs_inode *ni) +{ + int attrsz; + int res; + le32 securid; + ntfs_attr *na; + + /* + * upgrade requires NTFS format v3.x + * also refuse upgrading for special files + * whose number is less than FILE_first_user + */ + + if ((vol->major_ver >= 3) + && (ni->mft_no >= FILE_first_user)) { + attrsz = ntfs_attr_size(attr); + securid = setsecurityattr(vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)attr, + (s64)attrsz); + if (securid) { + na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, + AT_UNNAMED, 0); + if (na) { + /* expand standard information attribute to v3.x */ + res = ntfs_attr_truncate(na, + (s64)sizeof(STANDARD_INFORMATION)); + ni->owner_id = const_cpu_to_le32(0); + ni->quota_charged = const_cpu_to_le64(0); + ni->usn = const_cpu_to_le64(0); + ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0); + set_nino_flag(ni, v3_Extensions); + ni->security_id = securid; + ntfs_attr_close(na); + } else { + ntfs_log_error("Failed to upgrade " + "standard informations\n"); + errno = EIO; + res = -1; + } + } else + res = -1; + /* mark node as dirty */ + NInoSetDirty(ni); + } else + res = 1; + + return (res); +} + +/* + * Optional simplified checking of group membership + * + * This only takes into account the groups defined in + * /etc/group at initialization time. + * It does not take into account the groups dynamically set by + * setgroups() nor the changes in /etc/group since initialization + * + * This optional method could be useful if standard checking + * leads to a performance concern. + * + * Should not be called for user root, however the group may be root + * + */ + +static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) +{ + BOOL ingroup; + int grcnt; + gid_t *groups; + struct MAPPING *user; + + ingroup = FALSE; + if (uid) { + user = scx->mapping[MAPUSERS]; + while (user && ((uid_t)user->xid != uid)) + user = user->next; + if (user) { + groups = user->groups; + grcnt = user->grcnt; + while ((--grcnt >= 0) && (groups[grcnt] != gid)) { } + ingroup = (grcnt >= 0); + } + } + return (ingroup); +} + +#if defined(__sun) && defined (__SVR4) + +/* + * Check whether current thread owner is member of file group + * Solaris/OpenIndiana version + * Should not be called for user root, however the group may be root + * + * The group list is available in "/proc/$PID/cred" + * + */ + +static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) +{ + typedef struct prcred { + uid_t pr_euid; /* effective user id */ + uid_t pr_ruid; /* real user id */ + uid_t pr_suid; /* saved user id (from exec) */ + gid_t pr_egid; /* effective group id */ + gid_t pr_rgid; /* real group id */ + gid_t pr_sgid; /* saved group id (from exec) */ + int pr_ngroups; /* number of supplementary groups */ + gid_t pr_groups[1]; /* array of supplementary groups */ + } prcred_t; + enum { readset = 16 }; + + prcred_t basecreds; + gid_t groups[readset]; + char filename[64]; + int fd; + int k; + int cnt; + gid_t *p; + BOOL ismember; + int got; + pid_t tid; + + if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS)) + ismember = staticgroupmember(scx, uid, gid); + else { + ismember = FALSE; /* default return */ + tid = scx->tid; + sprintf(filename,"/proc/%u/cred",tid); + fd = open(filename,O_RDONLY); + if (fd >= 0) { + got = read(fd, &basecreds, sizeof(prcred_t)); + if (got == sizeof(prcred_t)) { + if (basecreds.pr_egid == gid) + ismember = TRUE; + p = basecreds.pr_groups; + cnt = 1; + k = 0; + while (!ismember + && (k < basecreds.pr_ngroups) + && (cnt > 0) + && (*p != gid)) { + k++; + cnt--; + p++; + if (cnt <= 0) { + got = read(fd, groups, + readset*sizeof(gid_t)); + cnt = got/sizeof(gid_t); + p = groups; + } + } + if ((cnt > 0) + && (k < basecreds.pr_ngroups)) + ismember = TRUE; + } + close(fd); + } + } + return (ismember); +} + +#else /* defined(__sun) && defined (__SVR4) */ + +/* + * Check whether current thread owner is member of file group + * Linux version + * Should not be called for user root, however the group may be root + * + * As indicated by Miklos Szeredi : + * + * The group list is available in + * + * /proc/$PID/task/$TID/status + * + * and fuse supplies TID in get_fuse_context()->pid. The only problem is + * finding out PID, for which I have no good solution, except to iterate + * through all processes. This is rather slow, but may be speeded up + * with caching and heuristics (for single threaded programs PID = TID). + * + * The following implementation gets the group list from + * /proc/$TID/task/$TID/status which apparently exists and + * contains the same data. + */ + +static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) +{ + static char key[] = "\nGroups:"; + char buf[BUFSZ+1]; + char filename[64]; + enum { INKEY, INSEP, INNUM, INEND } state; + int fd; + char c; + int matched; + BOOL ismember; + int got; + char *p; + gid_t grp; + pid_t tid; + + if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS)) + ismember = staticgroupmember(scx, uid, gid); + else { + ismember = FALSE; /* default return */ + tid = scx->tid; + sprintf(filename,"/proc/%u/task/%u/status",tid,tid); + fd = open(filename,O_RDONLY); + if (fd >= 0) { + got = read(fd, buf, BUFSZ); + buf[got] = 0; + state = INKEY; + matched = 0; + p = buf; + grp = 0; + /* + * A simple automaton to process lines like + * Groups: 14 500 513 + */ + do { + c = *p++; + if (!c) { + /* refill buffer */ + got = read(fd, buf, BUFSZ); + buf[got] = 0; + p = buf; + c = *p++; /* 0 at end of file */ + } + switch (state) { + case INKEY : + if (key[matched] == c) { + if (!key[++matched]) + state = INSEP; + } else + if (key[0] == c) + matched = 1; + else + matched = 0; + break; + case INSEP : + if ((c >= '0') && (c <= '9')) { + grp = c - '0'; + state = INNUM; + } else + if ((c != ' ') && (c != '\t')) + state = INEND; + break; + case INNUM : + if ((c >= '0') && (c <= '9')) + grp = grp*10 + c - '0'; + else { + ismember = (grp == gid); + if ((c != ' ') && (c != '\t')) + state = INEND; + else + state = INSEP; + } + default : + break; + } + } while (!ismember && c && (state != INEND)); + close(fd); + if (!c) + ntfs_log_error("No group record found in %s\n",filename); + } else + ntfs_log_error("Could not open %s\n",filename); + } + return (ismember); +} + +#endif /* defined(__sun) && defined (__SVR4) */ + +#if POSIXACLS + +/* + * Extract the basic permissions from a Posix ACL + * + * This is only to be used when Posix ACLs are compiled in, + * but not enabled in the mount options. + * + * it replaces the permission mask by the group permissions. + * If special groups are mapped, they are also considered as world. + */ + +static int ntfs_basic_perms(const struct SECURITY_CONTEXT *scx, + const struct POSIX_SECURITY *pxdesc) +{ + int k; + int perms; + const struct POSIX_ACE *pace; + const struct MAPPING* group; + + k = 0; + perms = pxdesc->mode; + for (k=0; k < pxdesc->acccnt; k++) { + pace = &pxdesc->acl.ace[k]; + if (pace->tag == POSIX_ACL_GROUP_OBJ) + perms = (perms & 07707) + | ((pace->perms & 7) << 3); + else + if (pace->tag == POSIX_ACL_GROUP) { + group = scx->mapping[MAPGROUPS]; + while (group && (group->xid != pace->id)) + group = group->next; + if (group && group->grcnt + && (*(group->groups) == (gid_t)pace->id)) + perms |= pace->perms & 7; + } + } + return (perms); +} + +#endif /* POSIXACLS */ + +/* + * Cacheing is done two-way : + * - from uid, gid and perm to securid (CACHED_SECURID) + * - from a securid to uid, gid and perm (CACHED_PERMISSIONS) + * + * CACHED_SECURID data is kept in a most-recent-first list + * which should not be too long to be efficient. Its optimal + * size is depends on usage and is hard to determine. + * + * CACHED_PERMISSIONS data is kept in a two-level indexed array. It + * is optimal at the expense of storage. Use of a most-recent-first + * list would save memory and provide similar performances for + * standard usage, but not for file servers with too many file + * owners + * + * CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS + * for legacy directories which were not allocated a security_id + * it is organized in a most-recent-first list. + * + * In main caches, data is never invalidated, as the meaning of + * a security_id only changes when user mapping is changed, which + * current implies remounting. However returned entries may be + * overwritten at next update, so data has to be copied elsewhere + * before another cache update is made. + * In legacy cache, data has to be invalidated when protection is + * changed. + * + * Though the same data may be found in both list, they + * must be kept separately : the interpretation of ACL + * in both direction are approximations which could be non + * reciprocal for some configuration of the user mapping data + * + * During the process of recompiling ntfs-3g from a tgz archive, + * security processing added 7.6% to the cpu time used by ntfs-3g + * and 30% if the cache is disabled. + */ + +static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx, + u32 securindex) +{ + struct PERMISSIONS_CACHE *cache; + unsigned int index1; + unsigned int i; + + cache = (struct PERMISSIONS_CACHE*)NULL; + /* create the first permissions blocks */ + index1 = securindex >> CACHE_PERMISSIONS_BITS; + cache = (struct PERMISSIONS_CACHE*) + ntfs_malloc(sizeof(struct PERMISSIONS_CACHE) + + index1*sizeof(struct CACHED_PERMISSIONS*)); + if (cache) { + cache->head.last = index1; + cache->head.p_reads = 0; + cache->head.p_hits = 0; + cache->head.p_writes = 0; + *scx->pseccache = cache; + for (i=0; i<=index1; i++) + cache->cachetable[i] + = (struct CACHED_PERMISSIONS*)NULL; + } + return (cache); +} + +/* + * Free memory used by caches + * The only purpose is to facilitate the detection of memory leaks + */ + +static void free_caches(struct SECURITY_CONTEXT *scx) +{ + unsigned int index1; + struct PERMISSIONS_CACHE *pseccache; + + pseccache = *scx->pseccache; + if (pseccache) { + for (index1=0; index1<=pseccache->head.last; index1++) + if (pseccache->cachetable[index1]) { +#if POSIXACLS + struct CACHED_PERMISSIONS *cacheentry; + unsigned int index2; + + for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) { + cacheentry = &pseccache->cachetable[index1][index2]; + if (cacheentry->valid + && cacheentry->pxdesc) + free(cacheentry->pxdesc); + } +#endif + free(pseccache->cachetable[index1]); + } + free(pseccache); + } +} + +static int compare(const struct CACHED_SECURID *cached, + const struct CACHED_SECURID *item) +{ +#if POSIXACLS + size_t csize; + size_t isize; + + /* only compare data and sizes */ + csize = (cached->variable ? + sizeof(struct POSIX_ACL) + + (((struct POSIX_SECURITY*)cached->variable)->acccnt + + ((struct POSIX_SECURITY*)cached->variable)->defcnt) + *sizeof(struct POSIX_ACE) : + 0); + isize = (item->variable ? + sizeof(struct POSIX_ACL) + + (((struct POSIX_SECURITY*)item->variable)->acccnt + + ((struct POSIX_SECURITY*)item->variable)->defcnt) + *sizeof(struct POSIX_ACE) : + 0); + return ((cached->uid != item->uid) + || (cached->gid != item->gid) + || (cached->dmode != item->dmode) + || (csize != isize) + || (csize + && isize + && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl, + &((struct POSIX_SECURITY*)item->variable)->acl, csize))); +#else + return ((cached->uid != item->uid) + || (cached->gid != item->gid) + || (cached->dmode != item->dmode)); +#endif +} + +static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached, + const struct CACHED_PERMISSIONS_LEGACY *item) +{ + return (cached->mft_no != item->mft_no); +} + +/* + * Resize permission cache table + * do not call unless resizing is needed + * + * If allocation fails, the cache size is not updated + * Lack of memory is not considered as an error, the cache is left + * consistent and errno is not set. + */ + +static void resize_cache(struct SECURITY_CONTEXT *scx, + u32 securindex) +{ + struct PERMISSIONS_CACHE *oldcache; + struct PERMISSIONS_CACHE *newcache; + int newcnt; + int oldcnt; + unsigned int index1; + unsigned int i; + + oldcache = *scx->pseccache; + index1 = securindex >> CACHE_PERMISSIONS_BITS; + newcnt = index1 + 1; + if (newcnt <= ((CACHE_PERMISSIONS_SIZE + + (1 << CACHE_PERMISSIONS_BITS) + - 1) >> CACHE_PERMISSIONS_BITS)) { + /* expand cache beyond current end, do not use realloc() */ + /* to avoid losing data when there is no more memory */ + oldcnt = oldcache->head.last + 1; + newcache = (struct PERMISSIONS_CACHE*) + ntfs_malloc( + sizeof(struct PERMISSIONS_CACHE) + + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*)); + if (newcache) { + memcpy(newcache,oldcache, + sizeof(struct PERMISSIONS_CACHE) + + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*)); + free(oldcache); + /* mark new entries as not valid */ + for (i=newcache->head.last+1; i<=index1; i++) + newcache->cachetable[i] + = (struct CACHED_PERMISSIONS*)NULL; + newcache->head.last = index1; + *scx->pseccache = newcache; + } + } +} + +/* + * Enter uid, gid and mode into cache, if possible + * + * returns the updated or created cache entry, + * or NULL if not possible (typically if there is no + * security id associated) + */ + +#if POSIXACLS +static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + struct POSIX_SECURITY *pxdesc) +#else +static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode) +#endif +{ + struct CACHED_PERMISSIONS *cacheentry; + struct CACHED_PERMISSIONS *cacheblock; + struct PERMISSIONS_CACHE *pcache; + u32 securindex; +#if POSIXACLS + int pxsize; + struct POSIX_SECURITY *pxcached; +#endif + unsigned int index1; + unsigned int index2; + int i; + + /* cacheing is only possible if a security_id has been defined */ + if (test_nino_flag(ni, v3_Extensions) + && ni->security_id) { + /* + * Immediately test the most frequent situation + * where the entry exists + */ + securindex = le32_to_cpu(ni->security_id); + index1 = securindex >> CACHE_PERMISSIONS_BITS; + index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1); + pcache = *scx->pseccache; + if (pcache + && (pcache->head.last >= index1) + && pcache->cachetable[index1]) { + cacheentry = &pcache->cachetable[index1][index2]; + cacheentry->uid = uid; + cacheentry->gid = gid; +#if POSIXACLS + if (cacheentry->valid && cacheentry->pxdesc) + free(cacheentry->pxdesc); + if (pxdesc) { + pxsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + pxcached = (struct POSIX_SECURITY*)malloc(pxsize); + if (pxcached) { + memcpy(pxcached, pxdesc, pxsize); + cacheentry->pxdesc = pxcached; + } else { + cacheentry->valid = 0; + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + cacheentry->mode = pxdesc->mode & 07777; + } else + cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL; +#else + cacheentry->mode = mode & 07777; +#endif + cacheentry->inh_fileid = const_cpu_to_le32(0); + cacheentry->inh_dirid = const_cpu_to_le32(0); + cacheentry->valid = 1; + pcache->head.p_writes++; + } else { + if (!pcache) { + /* create the first cache block */ + pcache = create_caches(scx, securindex); + } else { + if (index1 > pcache->head.last) { + resize_cache(scx, securindex); + pcache = *scx->pseccache; + } + } + /* allocate block, if cache table was allocated */ + if (pcache && (index1 <= pcache->head.last)) { + cacheblock = (struct CACHED_PERMISSIONS*) + malloc(sizeof(struct CACHED_PERMISSIONS) + << CACHE_PERMISSIONS_BITS); + pcache->cachetable[index1] = cacheblock; + for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++) + cacheblock[i].valid = 0; + cacheentry = &cacheblock[index2]; + if (cacheentry) { + cacheentry->uid = uid; + cacheentry->gid = gid; +#if POSIXACLS + if (pxdesc) { + pxsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + pxcached = (struct POSIX_SECURITY*)malloc(pxsize); + if (pxcached) { + memcpy(pxcached, pxdesc, pxsize); + cacheentry->pxdesc = pxcached; + } else { + cacheentry->valid = 0; + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + cacheentry->mode = pxdesc->mode & 07777; + } else + cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL; +#else + cacheentry->mode = mode & 07777; +#endif + cacheentry->inh_fileid = const_cpu_to_le32(0); + cacheentry->inh_dirid = const_cpu_to_le32(0); + cacheentry->valid = 1; + pcache->head.p_writes++; + } + } else + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } + } else { + cacheentry = (struct CACHED_PERMISSIONS*)NULL; +#if CACHE_LEGACY_SIZE + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + struct CACHED_PERMISSIONS_LEGACY wanted; + struct CACHED_PERMISSIONS_LEGACY *legacy; + + wanted.perm.uid = uid; + wanted.perm.gid = gid; +#if POSIXACLS + wanted.perm.mode = pxdesc->mode & 07777; + wanted.perm.inh_fileid = const_cpu_to_le32(0); + wanted.perm.inh_dirid = const_cpu_to_le32(0); + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)pxdesc; + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); +#else + wanted.perm.mode = mode & 07777; + wanted.perm.inh_fileid = const_cpu_to_le32(0); + wanted.perm.inh_dirid = const_cpu_to_le32(0); + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)NULL; + wanted.varsize = 0; +#endif + legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache( + scx->vol->legacy_cache, GENERIC(&wanted), + (cache_compare)leg_compare); + if (legacy) { + cacheentry = &legacy->perm; +#if POSIXACLS + /* + * give direct access to the cached pxdesc + * in the permissions structure + */ + cacheentry->pxdesc = legacy->variable; +#endif + } + } +#endif + } + return (cacheentry); +} + +/* + * Fetch owner, group and permission of a file, if cached + * + * Beware : do not use the returned entry after a cache update : + * the cache may be relocated making the returned entry meaningless + * + * returns the cache entry, or NULL if not available + */ + +static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni) +{ + struct CACHED_PERMISSIONS *cacheentry; + struct PERMISSIONS_CACHE *pcache; + u32 securindex; + unsigned int index1; + unsigned int index2; + + /* cacheing is only possible if a security_id has been defined */ + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + if (test_nino_flag(ni, v3_Extensions) + && (ni->security_id)) { + securindex = le32_to_cpu(ni->security_id); + index1 = securindex >> CACHE_PERMISSIONS_BITS; + index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1); + pcache = *scx->pseccache; + if (pcache + && (pcache->head.last >= index1) + && pcache->cachetable[index1]) { + cacheentry = &pcache->cachetable[index1][index2]; + /* reject if entry is not valid */ + if (!cacheentry->valid) + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + else + pcache->head.p_hits++; + if (pcache) + pcache->head.p_reads++; + } + } +#if CACHE_LEGACY_SIZE + else { + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + struct CACHED_PERMISSIONS_LEGACY wanted; + struct CACHED_PERMISSIONS_LEGACY *legacy; + + wanted.mft_no = ni->mft_no; + wanted.variable = (void*)NULL; + wanted.varsize = 0; + legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache( + scx->vol->legacy_cache, GENERIC(&wanted), + (cache_compare)leg_compare); + if (legacy) cacheentry = &legacy->perm; + } + } +#endif +#if POSIXACLS + if (cacheentry && !cacheentry->pxdesc) { + ntfs_log_error("No Posix descriptor in cache\n"); + cacheentry = (struct CACHED_PERMISSIONS*)NULL; + } +#endif + return (cacheentry); +} + +/* + * Retrieve a security attribute from $Secure + */ + +static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id) +{ + struct SII *psii; + union { + struct { + le32 dataoffsl; + le32 dataoffsh; + } parts; + le64 all; + } realign; + int found; + size_t size; + size_t rdsize; + s64 offs; + ntfs_inode *ni; + ntfs_index_context *xsii; + char *securattr; + + securattr = (char*)NULL; + ni = vol->secure_ni; + xsii = vol->secure_xsii; + if (ni && xsii) { + ntfs_index_ctx_reinit(xsii); + found = + !ntfs_index_lookup((char*)&id, + sizeof(SII_INDEX_KEY), xsii); + if (found) { + psii = (struct SII*)xsii->entry; + size = + (size_t) le32_to_cpu(psii->datasize) + - sizeof(SECURITY_DESCRIPTOR_HEADER); + /* work around bad alignment problem */ + realign.parts.dataoffsh = psii->dataoffsh; + realign.parts.dataoffsl = psii->dataoffsl; + offs = le64_to_cpu(realign.all) + + sizeof(SECURITY_DESCRIPTOR_HEADER); + + securattr = (char*)ntfs_malloc(size); + if (securattr) { + rdsize = ntfs_attr_data_read( + ni, STREAM_SDS, 4, + securattr, size, offs); + if ((rdsize != size) + || !ntfs_valid_descr(securattr, + rdsize)) { + /* error to be logged by caller */ + free(securattr); + securattr = (char*)NULL; + } + } + } else + if (errno != ENOENT) + ntfs_log_perror("Inconsistency in index $SII"); + } + if (!securattr) { + ntfs_log_error("Failed to retrieve a security descriptor\n"); + errno = EIO; + } + return (securattr); +} + +/* + * Get the security descriptor associated to a file + * + * Either : + * - read the security descriptor attribute (v1.x format) + * - or find the descriptor in $Secure:$SDS (v3.x format) + * + * in both case, sanity checks are done on the attribute and + * the descriptor can be assumed safe + * + * The returned descriptor is dynamically allocated and has to be freed + */ + +static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni) +{ + SII_INDEX_KEY securid; + char *securattr; + s64 readallsz; + + /* + * Warning : in some situations, after fixing by chkdsk, + * v3_Extensions are marked present (long standard informations) + * with a default security descriptor inserted in an + * attribute + */ + if (test_nino_flag(ni, v3_Extensions) + && vol->secure_ni && ni->security_id) { + /* get v3.x descriptor in $Secure */ + securid.security_id = ni->security_id; + securattr = retrievesecurityattr(vol,securid); + if (!securattr) + ntfs_log_error("Bad security descriptor for 0x%lx\n", + (long)le32_to_cpu(ni->security_id)); + } else { + /* get v1.x security attribute */ + readallsz = 0; + securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR, + AT_UNNAMED, 0, &readallsz); + if (securattr && !ntfs_valid_descr(securattr, readallsz)) { + ntfs_log_error("Bad security descriptor for inode %lld\n", + (long long)ni->mft_no); + free(securattr); + securattr = (char*)NULL; + } + } + if (!securattr) { + /* + * in some situations, there is no security + * descriptor, and chkdsk does not detect or fix + * anything. This could be a normal situation. + * When this happens, simulate a descriptor with + * minimum rights, so that a real descriptor can + * be created by chown or chmod + */ + ntfs_log_error("No security descriptor found for inode %lld\n", + (long long)ni->mft_no); + securattr = ntfs_build_descr(0, 0, adminsid, adminsid); + } + return (securattr); +} + +#if POSIXACLS + +/* + * Determine which access types to a file are allowed + * according to the relation of current process to the file + * + * When Posix ACLs are compiled in but not enabled in the mount + * options POSIX_ACL_USER, POSIX_ACL_GROUP and POSIX_ACL_MASK + * are ignored. + */ + +static int access_check_posix(struct SECURITY_CONTEXT *scx, + struct POSIX_SECURITY *pxdesc, mode_t request, + uid_t uid, gid_t gid) +{ + struct POSIX_ACE *pxace; + int userperms; + int groupperms; + int mask; + BOOL somegroup; + BOOL needgroups; + BOOL noacl; + mode_t perms; + int i; + + noacl = !(scx->vol->secure_flags & (1 << SECURITY_ACL)); + if (noacl) + perms = ntfs_basic_perms(scx, pxdesc); + else + perms = pxdesc->mode; + /* owner and root access */ + if (!scx->uid || (uid == scx->uid)) { + if (!scx->uid) { + /* root access if owner or other execution */ + if (perms & 0101) + perms |= 01777; + else { + /* root access if some group execution */ + groupperms = 0; + mask = 7; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + switch (pxace->tag) { + case POSIX_ACL_USER_OBJ : + case POSIX_ACL_GROUP_OBJ : + groupperms |= pxace->perms; + break; + case POSIX_ACL_GROUP : + if (!noacl) + groupperms + |= pxace->perms; + break; + case POSIX_ACL_MASK : + if (!noacl) + mask = pxace->perms & 7; + break; + default : + break; + } + } + perms = (groupperms & mask & 1) | 6; + } + } else + perms &= 07700; + } else { + /* + * analyze designated users, get mask + * and identify whether we need to check + * the group memberships. The groups are + * not needed when all groups have the + * same permissions as other for the + * requested modes. + */ + userperms = -1; + groupperms = -1; + needgroups = FALSE; + mask = 7; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + switch (pxace->tag) { + case POSIX_ACL_USER : + if (!noacl + && ((uid_t)pxace->id == scx->uid)) + userperms = pxace->perms; + break; + case POSIX_ACL_MASK : + if (!noacl) + mask = pxace->perms & 7; + break; + case POSIX_ACL_GROUP_OBJ : + if (((pxace->perms & mask) ^ perms) + & (request >> 6) & 7) + needgroups = TRUE; + break; + case POSIX_ACL_GROUP : + if (!noacl + && (((pxace->perms & mask) ^ perms) + & (request >> 6) & 7)) + needgroups = TRUE; + break; + default : + break; + } + } + /* designated users */ + if (userperms >= 0) + perms = (perms & 07000) + (userperms & mask); + else if (!needgroups) + perms &= 07007; + else { + /* owning group */ + if (!(~(perms >> 3) & request & mask) + && ((gid == scx->gid) + || groupmember(scx, scx->uid, gid))) + perms &= 07070; + else if (!noacl) { + /* other groups */ + groupperms = -1; + somegroup = FALSE; + for (i=pxdesc->acccnt-1; i>=0 ; i--) { + pxace = &pxdesc->acl.ace[i]; + if ((pxace->tag == POSIX_ACL_GROUP) + && groupmember(scx, scx->uid, pxace->id)) { + if (!(~pxace->perms & request & mask)) + groupperms = pxace->perms; + somegroup = TRUE; + } + } + if (groupperms >= 0) + perms = (perms & 07000) + (groupperms & mask); + else + if (somegroup) + perms = 0; + else + perms &= 07007; + } else + perms &= 07007; + } + } + return (perms); +} + +/* + * Get permissions to access a file + * Takes into account the relation of user to file (owner, group, ...) + * Do no use as mode of the file + * Do no call if default_permissions is set + * + * returns -1 if there is a problem + */ + +static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, + ntfs_inode * ni, mode_t request) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + uid_t uid; + gid_t gid; + int perm; + BOOL isdir; + struct POSIX_SECURITY *pxdesc; + + if (!scx->mapping[MAPUSERS]) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + perm = access_check_posix(scx,cached->pxdesc,request,uid,gid); + } else { + perm = 0; /* default to no permission */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, isdir); + if (pxdesc) + perm = pxdesc->mode & 07777; + else + perm = -1; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + pxdesc = ntfs_build_permissions_posix(scx,securattr, + usid, gsid, isdir); + if (pxdesc) + perm = pxdesc->mode & 07777; + else + perm = -1; + if (!perm && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + if (uid) + perm = 0700; + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (perm >= 0) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(ni, v3_Extensions) + && (perm >= 0)) { + enter_cache(scx, ni, uid, + gid, pxdesc); + } + if (pxdesc) { + perm = access_check_posix(scx,pxdesc,request,uid,gid); + free(pxdesc); + } + free(securattr); + } else { + perm = -1; + uid = gid = 0; + } + } + } + return (perm); +} + +/* + * Get a Posix ACL + * + * returns size or -errno if there is a problem + * if size was too small, no copy is done and errno is not set, + * the caller is expected to issue a new call + */ + +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + uid_t uid; + gid_t gid; + BOOL isdir; + size_t outsize; + + outsize = 0; /* default to error */ + if (!scx->mapping[MAPUSERS]) + errno = ENOTSUP; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) + pxdesc = cached->pxdesc; + else { + securattr = getsecurityattr(scx->vol, ni); + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + if (securattr) { + phead = + (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; +#endif + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, isdir); + + /* + * fetch owner and group for cacheing + */ + if (pxdesc) { + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + } +#if OWNERFROMACL + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + if (!(pxdesc->mode & 07777) + && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, + securattr); + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS) + enter_cache(scx, ni, uid, + gid, pxdesc); + } + free(securattr); + } else + pxdesc = (struct POSIX_SECURITY*)NULL; + } + + if (pxdesc) { + if (ntfs_valid_posix(pxdesc)) { + if (!strcmp(name,"system.posix_acl_default")) { + if (ni->mrec->flags + & MFT_RECORD_IS_DIRECTORY) + outsize = sizeof(struct POSIX_ACL) + + pxdesc->defcnt*sizeof(struct POSIX_ACE); + else { + /* + * getting default ACL from plain file : + * return EACCES if size > 0 as + * indicated in the man, but return ok + * if size == 0, so that ls does not + * display an error + */ + if (size > 0) { + outsize = 0; + errno = EACCES; + } else + outsize = sizeof(struct POSIX_ACL); + } + if (outsize && (outsize <= size)) { + memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL)); + memcpy(&value[sizeof(struct POSIX_ACL)], + &pxdesc->acl.ace[pxdesc->firstdef], + outsize-sizeof(struct POSIX_ACL)); + } + } else { + outsize = sizeof(struct POSIX_ACL) + + pxdesc->acccnt*sizeof(struct POSIX_ACE); + if (outsize <= size) + memcpy(value,&pxdesc->acl,outsize); + } + } else { + outsize = 0; + errno = EIO; + ntfs_log_error("Invalid Posix ACL built\n"); + } + if (!cached) + free(pxdesc); + } else + outsize = 0; + } + return (outsize ? (int)outsize : -errno); +} + +#else /* POSIXACLS */ + + +/* + * Get permissions to access a file + * Takes into account the relation of user to file (owner, group, ...) + * Do no use as mode of the file + * + * returns -1 if there is a problem + */ + +static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, mode_t request) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + BOOL isdir; + uid_t uid; + gid_t gid; + int perm; + + if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC))) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { + perm = cached->mode; + uid = cached->uid; + gid = cached->gid; + } else { + perm = 0; /* default to no permission */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); + if (!perm && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + if (uid) + perm = 0700; + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (perm >= 0) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(ni, v3_Extensions) + && (perm >= 0)) { + enter_cache(scx, ni, uid, + gid, perm); + } + free(securattr); + } else { + perm = -1; + uid = gid = 0; + } + } + if (perm >= 0) { + if (!scx->uid) { + /* root access and execution */ + if (perm & 0111) + perm |= 01777; + else + perm = 0; + } else + if (uid == scx->uid) + perm &= 07700; + else + /* + * avoid checking group membership + * when the requested perms for group + * are the same as perms for other + */ + if ((gid == scx->gid) + || ((((perm >> 3) ^ perm) + & (request >> 6) & 7) + && groupmember(scx, scx->uid, gid))) + perm &= 07070; + else + perm &= 07007; + } + } + return (perm); +} + +#endif /* POSIXACLS */ + +/* + * Get an NTFS ACL + * + * Returns size or -errno if there is a problem + * if size was too small, no copy is done and errno is not set, + * the caller is expected to issue a new call + */ + +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size) +{ + char *securattr; + size_t outsize; + + outsize = 0; /* default to no data and no error */ + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + outsize = ntfs_attr_size(securattr); + if (outsize <= size) { + memcpy(value,securattr,outsize); + } + free(securattr); + } + return (outsize ? (int)outsize : -errno); +} + +/* + * Get owner, group and permissions in an stat structure + * returns permissions, or -1 if there is a problem + */ + +int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, + ntfs_inode * ni, struct stat *stbuf) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + char *securattr; + const SID *usid; /* owner of file/directory */ + const SID *gsid; /* group of file/directory */ + const struct CACHED_PERMISSIONS *cached; + int perm; + BOOL isdir; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + + if (!scx->mapping[MAPUSERS]) + perm = 07777; + else { + /* check whether available in cache */ + cached = fetch_cache(scx,ni); + if (cached) { +#if POSIXACLS + if (!(scx->vol->secure_flags & (1 << SECURITY_ACL)) + && cached->pxdesc) + perm = ntfs_basic_perms(scx,cached->pxdesc); + else +#endif + perm = cached->mode; + stbuf->st_uid = cached->uid; + stbuf->st_gid = cached->gid; + stbuf->st_mode = (stbuf->st_mode & ~07777) + perm; + } else { + perm = -1; /* default to error */ + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = + (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; +#endif +#if POSIXACLS + pxdesc = ntfs_build_permissions_posix( + scx->mapping, securattr, + usid, gsid, isdir); + if (pxdesc) { + if (!(scx->vol->secure_flags + & (1 << SECURITY_ACL))) + perm = ntfs_basic_perms(scx, + pxdesc); + else + perm = pxdesc->mode & 07777; + } else + perm = -1; +#else + perm = ntfs_build_permissions(securattr, + usid, gsid, isdir); +#endif + /* + * fetch owner and group for cacheing + */ + if (perm >= 0) { + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, ni); + } +#if OWNERFROMACL + stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + if (!perm && ntfs_same_sid(usid, adminsid)) { + stbuf->st_uid = + find_tenant(scx, + securattr); + if (stbuf->st_uid) + perm = 0700; + } else + stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + stbuf->st_mode = + (stbuf->st_mode & ~07777) + perm; +#if POSIXACLS + enter_cache(scx, ni, stbuf->st_uid, + stbuf->st_gid, pxdesc); + free(pxdesc); +#else + enter_cache(scx, ni, stbuf->st_uid, + stbuf->st_gid, perm); +#endif + } + free(securattr); + } + } + } + return (perm); +} + +#if POSIXACLS + +/* + * Get the base for a Posix inheritance and + * build an inherited Posix descriptor + */ + +static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, mode_t mode, BOOL isdir) +{ + const struct CACHED_PERMISSIONS *cached; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + struct POSIX_SECURITY *pxdesc; + struct POSIX_SECURITY *pydesc; + char *securattr; + const SID *usid; + const SID *gsid; + uid_t uid; + gid_t gid; + + pydesc = (struct POSIX_SECURITY*)NULL; + /* check whether parent directory is available in cache */ + cached = fetch_cache(scx,dir_ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + pxdesc = cached->pxdesc; + if (pxdesc) { + if (scx->vol->secure_flags & (1 << SECURITY_ACL)) + pydesc = ntfs_build_inherited_posix(pxdesc, + mode, scx->umask, isdir); + else + pydesc = ntfs_build_basic_posix(pxdesc, + mode, scx->umask, isdir); + } + } else { + securattr = getsecurityattr(scx->vol, dir_ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + securattr; + gsid = (const SID*)& + securattr[le32_to_cpu(phead->group)]; + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if OWNERFROMACL + usid = ntfs_acl_owner(securattr); + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, TRUE); + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#else + usid = (const SID*)& + securattr[le32_to_cpu(phead->owner)]; + pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, + usid, gsid, TRUE); + if (pxdesc && ntfs_same_sid(usid, adminsid)) { + uid = find_tenant(scx, securattr); + } else + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); +#endif + if (pxdesc) { + /* + * Create a security id if there were none + * and upgrade option is selected + */ + if (!test_nino_flag(dir_ni, v3_Extensions) + && (scx->vol->secure_flags + & (1 << SECURITY_ADDSECURIDS))) { + upgrade_secur_desc(scx->vol, + securattr, dir_ni); + /* + * fetch owner and group for cacheing + * if there is a securid + */ + } + if (test_nino_flag(dir_ni, v3_Extensions)) { + enter_cache(scx, dir_ni, uid, + gid, pxdesc); + } + if (scx->vol->secure_flags + & (1 << SECURITY_ACL)) + pydesc = ntfs_build_inherited_posix( + pxdesc, mode, + scx->umask, isdir); + else + pydesc = ntfs_build_basic_posix( + pxdesc, mode, + scx->umask, isdir); + free(pxdesc); + } + free(securattr); + } + } + return (pydesc); +} + +/* + * Allocate a security_id for a file being created + * + * Returns zero if not possible (NTFS v3.x required) + */ + +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir) +{ +#if !FORCE_FORMAT_v1x + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + struct POSIX_SECURITY *pxdesc; + char *newattr; + int newattrsz; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + le32 securid; +#endif + + securid = const_cpu_to_le32(0); + +#if !FORCE_FORMAT_v1x + + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); + if (pxdesc) { + /* check whether target securid is known in cache */ + + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = pxdesc->mode & mode & 07777; + if (isdir) wanted.dmode |= 0x10000; + wanted.variable = (void*)pxdesc; + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) + securid = cached->securid; + + /* not in cache : make sure we can create ids */ + + if (!cached && (scx->vol->major_ver >= 3)) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + if (newattr) { + newattrsz = ntfs_attr_size(newattr); + securid = setsecurityattr(scx->vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + newattrsz); + if (securid) { + /* update cache, for subsequent use */ + wanted.securid = securid; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } + free(pxdesc); + } +#endif + return (securid); +} + +/* + * Apply Posix inheritance to a newly created file + * (for NTFS 1.x only : no securid) + */ + +int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, + ntfs_inode *dir_ni, mode_t mode) +{ + struct POSIX_SECURITY *pxdesc; + char *newattr; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + BOOL isdir; + int res; + + res = -1; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); + if (pxdesc) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + if (newattr) { + /* Adjust Windows read-only flag */ + res = update_secur_descr(scx->vol, newattr, ni); + if (!res && !isdir) { + if (mode & S_IWUSR) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + } +#if CACHE_LEGACY_SIZE + /* also invalidate legacy cache */ + if (isdir && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; + + legacy.mft_no = ni->mft_no; + legacy.variable = pxdesc; + legacy.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } +#endif + free(newattr); + + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } + return (res); +} + +#else + +le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, mode_t mode, BOOL isdir) +{ +#if !FORCE_FORMAT_v1x + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + char *newattr; + int newattrsz; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + le32 securid; +#endif + + securid = const_cpu_to_le32(0); + +#if !FORCE_FORMAT_v1x + /* check whether target securid is known in cache */ + + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = mode & 07777; + if (isdir) wanted.dmode |= 0x10000; + wanted.variable = (void*)NULL; + wanted.varsize = 0; + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) + securid = cached->securid; + + /* not in cache : make sure we can create ids */ + + if (!cached && (scx->vol->major_ver >= 3)) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File created by an unmapped user/group %d/%d\n", + (int)uid, (int)gid); + usid = gsid = adminsid; + } + newattr = ntfs_build_descr(mode, isdir, usid, gsid); + if (newattr) { + newattrsz = ntfs_attr_size(newattr); + securid = setsecurityattr(scx->vol, + (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, + newattrsz); + if (securid) { + /* update cache, for subsequent use */ + wanted.securid = securid; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + } + } +#endif + return (securid); +} + +#endif + +/* + * Update ownership and mode of a file, reusing an existing + * security descriptor when possible + * + * Returns zero if successful + */ + +#if POSIXACLS +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, mode_t mode, + struct POSIX_SECURITY *pxdesc) +#else +int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, mode_t mode) +#endif +{ + int res; + const struct CACHED_SECURID *cached; + struct CACHED_SECURID wanted; + char *newattr; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + BOOL isdir; + + res = 0; + + /* check whether target securid is known in cache */ + + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + wanted.uid = uid; + wanted.gid = gid; + wanted.dmode = mode & 07777; + if (isdir) wanted.dmode |= 0x10000; +#if POSIXACLS + wanted.variable = (void*)pxdesc; + if (pxdesc) + wanted.varsize = sizeof(struct POSIX_SECURITY) + + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); + else + wanted.varsize = 0; +#else + wanted.variable = (void*)NULL; + wanted.varsize = 0; +#endif + if (test_nino_flag(ni, v3_Extensions)) { + cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( + scx->vol->securid_cache, GENERIC(&wanted), + (cache_compare)compare); + /* quite simple, if we are lucky */ + if (cached) { + ni->security_id = cached->securid; + NInoSetDirty(ni); + } + } else cached = (struct CACHED_SECURID*)NULL; + + if (!cached) { + /* + * Do not use usid and gsid from former attributes, + * but recompute them to get repeatable results + * which can be kept in cache. + */ + usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); + if (!usid || !gsid) { + ntfs_log_error("File made owned by an unmapped user/group %d/%d\n", + uid, gid); + usid = gsid = adminsid; + } +#if POSIXACLS + if (pxdesc) + newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, + isdir, usid, gsid); + else + newattr = ntfs_build_descr(mode, + isdir, usid, gsid); +#else + newattr = ntfs_build_descr(mode, + isdir, usid, gsid); +#endif + if (newattr) { + res = update_secur_descr(scx->vol, newattr, ni); + if (!res) { + /* adjust Windows read-only flag */ + if (!isdir) { + if (mode & S_IWUSR) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + NInoFileNameSetDirty(ni); + } + /* update cache, for subsequent use */ + if (test_nino_flag(ni, v3_Extensions)) { + wanted.securid = ni->security_id; + ntfs_enter_cache(scx->vol->securid_cache, + GENERIC(&wanted), + (cache_compare)compare); + } +#if CACHE_LEGACY_SIZE + /* also invalidate legacy cache */ + if (isdir && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; + + legacy.mft_no = ni->mft_no; +#if POSIXACLS + legacy.variable = wanted.variable; + legacy.varsize = wanted.varsize; +#else + legacy.variable = (void*)NULL; + legacy.varsize = 0; +#endif + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } +#endif + } + free(newattr); + } else { + /* + * could not build new security attribute + * errno set by ntfs_build_descr() + */ + res = -1; + } + } + return (res); +} + +/* + * Check whether user has ownership rights on a file + * + * Returns TRUE if allowed + * if not, errno tells why + */ + +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni) +{ + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + uid_t processuid; + uid_t uid; + BOOL gotowner; + int allowed; + + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + /* + * Always allow for root + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS] || !processuid) + allowed = TRUE; + else { + gotowner = FALSE; /* default */ + /* get the owner, either from cache or from old attribute */ + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gotowner = TRUE; + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + const SECURITY_DESCRIPTOR_RELATIVE *phead; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + usid = (const SID*)&oldattr + [le32_to_cpu(phead->owner)]; +#endif + uid = ntfs_find_user(scx->mapping[MAPUSERS], + usid); + gotowner = TRUE; + free(oldattr); + } + } + allowed = FALSE; + if (gotowner) { +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (processuid == uid)) + allowed = TRUE; + else + errno = EPERM; + } + } + return (allowed); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +#if POSIXACLS + +/* + * Set a new access or default Posix ACL to a file + * (or remove ACL if no input data) + * Validity of input data is checked after merging + * + * Returns 0, or -1 if there is a problem which errno describes + */ + +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, const char *value, size_t size, + int flags) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + uid_t processuid; + const SID *usid; + const SID *gsid; + uid_t uid; + uid_t gid; + int res; + BOOL isdir; + BOOL deflt; + BOOL exist; + int count; + struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc; + + /* get the current pxsec, either from cache or from old attribute */ + res = -1; + deflt = !strcmp(name,"system.posix_acl_default"); + if (size) + count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE); + else + count = 0; + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + newpxdesc = (struct POSIX_SECURITY*)NULL; + if ((!value + || (((const struct POSIX_ACL*)value)->version == POSIX_VERSION)) + && (!deflt || isdir || (!size && !value))) { + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + newpxdesc = ntfs_replace_acl(oldpxdesc, + (const struct POSIX_ACL*)value,count,deflt); + } + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; +#endif + gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)]; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + oldpxdesc = ntfs_build_permissions_posix(scx->mapping, + oldattr, usid, gsid, isdir); + if (oldpxdesc) { + if (deflt) + exist = oldpxdesc->defcnt > 0; + else + exist = oldpxdesc->acccnt > 3; + if ((exist && (flags & XATTR_CREATE)) + || (!exist && (flags & XATTR_REPLACE))) { + errno = (exist ? EEXIST : ENODATA); + } else { + newpxdesc = ntfs_replace_acl(oldpxdesc, + (const struct POSIX_ACL*)value,count,deflt); + } + free(oldpxdesc); + } + free(oldattr); + } + } + } else + errno = EINVAL; + + if (newpxdesc) { + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (uid == processuid)) { + /* + * clear setgid if file group does + * not match process group + */ + if (processuid && (gid != scx->gid) + && !groupmember(scx, scx->uid, gid)) { + newpxdesc->mode &= ~S_ISGID; + } + res = ntfs_set_owner_mode(scx, ni, uid, gid, + newpxdesc->mode, newpxdesc); + } else + errno = EPERM; + free(newpxdesc); + } + return (res ? -1 : 0); +} + +/* + * Remove a default Posix ACL from a file + * + * Returns 0, or -1 if there is a problem which errno describes + */ + +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name) +{ + return (ntfs_set_posix_acl(scx, ni, name, + (const char*)NULL, 0, 0)); +} + +#endif + +/* + * Set a new NTFS ACL to a file + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + char *attr; + int res; + + res = -1; + if ((size > 0) + && !(flags & XATTR_CREATE) + && ntfs_valid_descr(value,size) + && (ntfs_attr_size(value) == size)) { + /* need copying in order to write */ + attr = (char*)ntfs_malloc(size); + if (attr) { + memcpy(attr,value,size); + res = update_secur_descr(scx->vol, attr, ni); + /* + * No need to invalidate standard caches : + * the relation between a securid and + * the associated protection is unchanged, + * only the relation between a file and + * its securid and protection is changed. + */ +#if CACHE_LEGACY_SIZE + /* + * we must however invalidate the legacy + * cache, which is based on inode numbers. + * For safety, invalidate even if updating + * failed. + */ + if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + && !ni->security_id) { + struct CACHED_PERMISSIONS_LEGACY legacy; + + legacy.mft_no = ni->mft_no; + legacy.variable = (char*)NULL; + legacy.varsize = 0; + ntfs_invalidate_cache(scx->vol->legacy_cache, + GENERIC(&legacy), + (cache_compare)leg_compare,0); + } +#endif + free(attr); + } else + errno = ENOMEM; + } else + errno = EINVAL; + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Set new permissions to a file + * Checks user mapping has been defined before request for setting + * + * rejected if request is not originated by owner or root + * + * returns 0 on success + * -1 on failure, with errno = EIO + */ + +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t processuid; + uid_t uid; + uid_t gid; + int res; +#if POSIXACLS + BOOL isdir; + int pxsize; + const struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; +#endif + + /* get the current owner, either from cache or from old attribute */ + res = 0; + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gid = cached->gid; +#if POSIXACLS + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + /* must copy before merging */ + pxsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); + if (newpxdesc) { + memcpy(newpxdesc, oldpxdesc, pxsize); + if (ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + } else + res = -1; + } else + newpxdesc = (struct POSIX_SECURITY*)NULL; +#endif + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; +#endif + gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)]; + uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); +#if POSIXACLS + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); + newpxdesc = ntfs_build_permissions_posix(scx->mapping, + oldattr, usid, gsid, isdir); + if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; +#endif + free(oldattr); + } else + res = -1; + } + + if (!res) { + processuid = scx->uid; +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (uid == processuid)) { + /* + * clear setgid if file group does + * not match process group + */ + if (processuid && (gid != scx->gid) + && !groupmember(scx, scx->uid, gid)) + mode &= ~S_ISGID; +#if POSIXACLS + if (newpxdesc) { + newpxdesc->mode = mode; + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); + } else + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); +#else + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); +#endif + } else { + errno = EPERM; + res = -1; /* neither owner nor root */ + } + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } +#if POSIXACLS + if (newpxdesc) free(newpxdesc); +#endif + return (res ? -1 : 0); +} + +/* + * Create a default security descriptor for files whose descriptor + * cannot be inherited + */ + +int ntfs_sd_add_everyone(ntfs_inode *ni) +{ + /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */ + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *ace; + SID *sid; + int ret, sd_len; + + /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */ + /* + * Calculate security descriptor length. We have 2 sub-authorities in + * owner and group SIDs, but structure SID contain only one, so add + * 4 bytes to every SID. + */ + sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) + + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); + sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len); + if (!sd) + return -1; + + sd->revision = SECURITY_DESCRIPTOR_REVISION; + sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE; + + sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR)); + sid->revision = SID_REVISION; + sid->sub_authority_count = 2; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + sid->identifier_authority.value[5] = 5; + sd->owner = cpu_to_le32((u8*)sid - (u8*)sd); + + sid = (SID*)((u8*)sid + sizeof(SID) + 4); + sid->revision = SID_REVISION; + sid->sub_authority_count = 2; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + sid->identifier_authority.value[5] = 5; + sd->group = cpu_to_le32((u8*)sid - (u8*)sd); + + acl = (ACL*)((u8*)sid + sizeof(SID) + 4); + acl->revision = ACL_REVISION; + acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)); + acl->ace_count = const_cpu_to_le16(1); + sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd); + + ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE)); + ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */ + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 1; + ace->sid.sub_authority[0] = const_cpu_to_le32(0); + ace->sid.identifier_authority.value[5] = 1; + + ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd, + sd_len); + if (ret) + ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR"); + + free(sd); + return ret; +} + +/* + * Check whether user can access a file in a specific way + * + * Returns 1 if access is allowed, including user is root or no + * user mapping defined + * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX + * 0 and sets errno if there is a problem or if access + * is not allowed + * + * This is used for Posix ACL and checking creation of DOS file names + */ + +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, + int accesstype) /* access type required (S_Ixxx values) */ +{ + int perm; + int res; + int allow; + struct stat stbuf; + + /* + * Always allow for root unless execution is requested. + * (was checked by fuse until kernel 2.6.29) + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS] + || (!scx->uid + && (!(accesstype & S_IEXEC) + || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)))) + allow = 1; + else { + perm = ntfs_get_perm(scx, ni, accesstype); + if (perm >= 0) { + res = EACCES; + switch (accesstype) { + case S_IEXEC: + allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; + break; + case S_IWRITE: + allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0; + break; + case S_IWRITE + S_IEXEC: + allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD: + allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0; + break; + case S_IREAD + S_IEXEC: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD + S_IWRITE: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0); + break; + case S_IWRITE + S_IEXEC + S_ISVTX: + if (perm & S_ISVTX) { + if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) + && (stbuf.st_uid == scx->uid)) + allow = 1; + else + allow = 2; + } else + allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + case S_IREAD + S_IWRITE + S_IEXEC: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; + default : + res = EINVAL; + allow = 0; + break; + } + if (!allow) + errno = res; + } else + allow = 0; + } + return (allow); +} + +/* + * Check whether user can create a file (or directory) + * + * Returns TRUE if access is allowed, + * Also returns the gid and dsetgid applicable to the created file + */ + +int ntfs_allowed_create(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, gid_t *pgid, mode_t *pdsetgid) +{ + int perm; + int res; + int allow; + struct stat stbuf; + + /* + * Always allow for root. + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS]) + perm = 0777; + else + perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC); + if (!scx->mapping[MAPUSERS] + || !scx->uid) { + allow = 1; + } else { + perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC); + if (perm >= 0) { + res = EACCES; + allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + if (!allow) + errno = res; + } else + allow = 0; + } + *pgid = scx->gid; + *pdsetgid = 0; + /* return directory group if S_ISGID is set */ + if (allow && (perm & S_ISGID)) { + if (ntfs_get_owner_mode(scx, dir_ni, &stbuf) >= 0) { + *pdsetgid = stbuf.st_mode & S_ISGID; + if (perm & S_ISGID) + *pgid = stbuf.st_gid; + } + } + return (allow); +} + +#if 0 /* not needed any more */ + +/* + * Check whether user can access the parent directory + * of a file in a specific way + * + * Returns true if access is allowed, including user is root and + * no user mapping defined + * + * Sets errno if there is a problem or if not allowed + * + * This is used for Posix ACL and checking creation of DOS file names + */ + +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, + const char *path, int accesstype) +{ + int allow; + char *dirpath; + char *name; + ntfs_inode *ni; + ntfs_inode *dir_ni; + struct stat stbuf; + + allow = 0; + dirpath = strdup(path); + if (dirpath) { + /* the root of file system is seen as a parent of itself */ + /* is that correct ? */ + name = strrchr(dirpath, '/'); + *name = 0; + dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath); + if (dir_ni) { + allow = ntfs_allowed_access(scx, + dir_ni, accesstype); + ntfs_inode_close(dir_ni); + /* + * for an not-owned sticky directory, have to + * check whether file itself is owned + */ + if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX)) + && (allow == 2)) { + ni = ntfs_pathname_to_inode(scx->vol, NULL, + path); + allow = FALSE; + if (ni) { + allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) + && (stbuf.st_uid == scx->uid); + ntfs_inode_close(ni); + } + } + } + free(dirpath); + } + return (allow); /* errno is set if not allowed */ +} + +#endif + +/* + * Define a new owner/group to a file + * + * returns zero if successful + */ + +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t fileuid; + uid_t filegid; + mode_t mode; + int perm; + BOOL isdir; + int res; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; + BOOL pxdescbuilt = FALSE; +#endif + + res = 0; + /* get the current owner and mode from cache or security attributes */ + oldattr = (char*)NULL; + cached = fetch_cache(scx,ni); + if (cached) { + fileuid = cached->uid; + filegid = cached->gid; + mode = cached->mode; +#if POSIXACLS + pxdesc = cached->pxdesc; + if (!pxdesc) + res = -1; +#endif + } else { + fileuid = 0; + filegid = 0; + mode = 0; + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + gsid = (const SID*) + &oldattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*) + &oldattr[le32_to_cpu(phead->owner)]; +#endif +#if POSIXACLS + pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, + usid, gsid, isdir); + if (pxdesc) { + pxdescbuilt = TRUE; + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + mode = perm = pxdesc->mode; + } else + res = -1; +#else + mode = perm = ntfs_build_permissions(oldattr, + usid, gsid, isdir); + if (perm >= 0) { + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + } else + res = -1; +#endif + free(oldattr); + } else + res = -1; + } + if (!res) { + /* check requested by root */ + /* or chgrp requested by owner to an owned group */ + if (!scx->uid + || ((((int)uid < 0) || (uid == fileuid)) + && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) + && (fileuid == scx->uid))) { + /* replace by the new usid and gsid */ + /* or reuse old gid and sid for cacheing */ + if ((int)uid < 0) + uid = fileuid; + if ((int)gid < 0) + gid = filegid; +#if !defined(__sun) || !defined (__SVR4) + /* clear setuid and setgid if owner has changed */ + /* unless request originated by root */ + if (uid && (fileuid != uid)) + mode &= 01777; +#endif +#if POSIXACLS + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, pxdesc); +#else + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); +#endif + } else { + res = -1; /* neither owner nor root */ + errno = EPERM; + } +#if POSIXACLS + if (pxdescbuilt) + free(pxdesc); +#endif + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } + return (res ? -1 : 0); +} + +/* + * Define new owner/group and mode to a file + * + * returns zero if successful + */ + +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, const mode_t mode) +{ + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + uid_t fileuid; + uid_t filegid; + int res; +#if POSIXACLS + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const SID *usid; + const SID *gsid; + BOOL isdir; + const struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; + int pxsize; +#endif + + res = 0; + /* get the current owner and mode from cache or security attributes */ + oldattr = (char*)NULL; + cached = fetch_cache(scx,ni); + if (cached) { + fileuid = cached->uid; + filegid = cached->gid; +#if POSIXACLS + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + /* must copy before merging */ + pxsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); + if (newpxdesc) { + memcpy(newpxdesc, oldpxdesc, pxsize); + if (ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + } else + res = -1; + } +#endif + } else { + fileuid = 0; + filegid = 0; + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { +#if POSIXACLS + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + gsid = (const SID*) + &oldattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*) + &oldattr[le32_to_cpu(phead->owner)]; +#endif + newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, + usid, gsid, isdir); + if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + else { + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + } +#endif + free(oldattr); + } else + res = -1; + } + if (!res) { + /* check requested by root */ + /* or chgrp requested by owner to an owned group */ + if (!scx->uid + || ((((int)uid < 0) || (uid == fileuid)) + && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) + && (fileuid == scx->uid))) { + /* replace by the new usid and gsid */ + /* or reuse old gid and sid for cacheing */ + if ((int)uid < 0) + uid = fileuid; + if ((int)gid < 0) + gid = filegid; +#if POSIXACLS + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); +#else + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); +#endif + } else { + res = -1; /* neither owner nor root */ + errno = EPERM; + } + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } +#if POSIXACLS + free(newpxdesc); +#endif + return (res ? -1 : 0); +} + +/* + * Build a security id for a descriptor inherited from + * parent directory the Windows way + */ + +static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, + const char *parentattr, BOOL fordir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *pphead; + const ACL *ppacl; + const SID *usid; + const SID *gsid; + BIGSID defusid; + BIGSID defgsid; + int offpacl; + int offgroup; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + ACL *pnacl; + int parentattrsz; + char *newattr; + int newattrsz; + int aclsz; + int usidsz; + int gsidsz; + int pos; + le32 securid; + + parentattrsz = ntfs_attr_size(parentattr); + pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr; + if (scx->mapping[MAPUSERS]) { + usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid); + gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid); +#if OWNERFROMACL + /* Get approximation of parent owner when cannot map */ + if (!gsid) + gsid = adminsid; + if (!usid) { + usid = ntfs_acl_owner(parentattr); + if (!ntfs_is_user_sid(gsid)) + gsid = usid; + } +#else + /* Define owner as root when cannot map */ + if (!usid) + usid = adminsid; + if (!gsid) + gsid = adminsid; +#endif + } else { + /* + * If there is no user mapping and this is not a root + * user, we have to get owner and group from somewhere, + * and the parent directory has to contribute. + * Windows never has to do that, because it can always + * rely on a user mapping + */ + if (!scx->uid) + usid = adminsid; + else { +#if OWNERFROMACL + usid = ntfs_acl_owner(parentattr); +#else + int offowner; + + offowner = le32_to_cpu(pphead->owner); + usid = (const SID*)&parentattr[offowner]; +#endif + } + if (!scx->gid) + gsid = adminsid; + else { + offgroup = le32_to_cpu(pphead->group); + gsid = (const SID*)&parentattr[offgroup]; + } + } + /* + * new attribute is smaller than parent's + * except for differences in SIDs which appear in + * owner, group and possible grants and denials in + * generic creator-owner and creator-group ACEs. + * For directories, an ACE may be duplicated for + * access and inheritance, so we double the count. + */ + usidsz = ntfs_sid_size(usid); + gsidsz = ntfs_sid_size(gsid); + newattrsz = parentattrsz + 3*usidsz + 3*gsidsz; + if (fordir) + newattrsz *= 2; + newattr = (char*)ntfs_malloc(newattrsz); + if (newattr) { + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + pnhead->control = (pphead->control + & (SE_DACL_AUTO_INHERITED | SE_SACL_AUTO_INHERITED)) + | SE_SELF_RELATIVE; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + /* + * locate and inherit DACL + * do not test SE_DACL_PRESENT (wrong for "DR Watson") + */ + pnhead->dacl = const_cpu_to_le32(0); + if (pphead->dacl) { + offpacl = le32_to_cpu(pphead->dacl); + ppacl = (const ACL*)&parentattr[offpacl]; + pnacl = (ACL*)&newattr[pos]; + aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, + fordir, pphead->control + & SE_DACL_AUTO_INHERITED); + if (aclsz) { + pnhead->dacl = cpu_to_le32(pos); + pos += aclsz; + pnhead->control |= SE_DACL_PRESENT; + } + } + /* + * locate and inherit SACL + */ + pnhead->sacl = const_cpu_to_le32(0); + if (pphead->sacl) { + offpacl = le32_to_cpu(pphead->sacl); + ppacl = (const ACL*)&parentattr[offpacl]; + pnacl = (ACL*)&newattr[pos]; + aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, + fordir, pphead->control + & SE_SACL_AUTO_INHERITED); + if (aclsz) { + pnhead->sacl = cpu_to_le32(pos); + pos += aclsz; + pnhead->control |= SE_SACL_PRESENT; + } + } + /* + * inherit or redefine owner + */ + memcpy(&newattr[pos],usid,usidsz); + pnhead->owner = cpu_to_le32(pos); + pos += usidsz; + /* + * inherit or redefine group + */ + memcpy(&newattr[pos],gsid,gsidsz); + pnhead->group = cpu_to_le32(pos); + pos += gsidsz; + securid = setsecurityattr(scx->vol, + (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos); + free(newattr); + } else + securid = const_cpu_to_le32(0); + return (securid); +} + +/* + * Get an inherited security id + * + * For Windows compatibility, the normal initial permission setting + * may be inherited from the parent directory instead of being + * defined by the creation arguments. + * + * The following creates an inherited id for that purpose. + * + * Note : the owner and group of parent directory are also + * inherited (which is not the case on Windows) if no user mapping + * is defined. + * + * Returns the inherited id, or zero if not possible (eg on NTFS 1.x) + */ + +le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, BOOL fordir) +{ + struct CACHED_PERMISSIONS *cached; + char *parentattr; + le32 securid; + + securid = const_cpu_to_le32(0); + cached = (struct CACHED_PERMISSIONS*)NULL; + /* + * Try to get inherited id from cache, possible when + * the current process owns the parent directory + */ + if (test_nino_flag(dir_ni, v3_Extensions) + && dir_ni->security_id) { + cached = fetch_cache(scx, dir_ni); + if (cached + && (cached->uid == scx->uid) && (cached->gid == scx->gid)) + securid = (fordir ? cached->inh_dirid + : cached->inh_fileid); + } + /* + * Not cached or not available in cache, compute it all + * Note : if parent directory has no id, it is not cacheable + */ + if (!securid) { + parentattr = getsecurityattr(scx->vol, dir_ni); + if (parentattr) { + securid = build_inherited_id(scx, + parentattr, fordir); + free(parentattr); + /* + * Store the result into cache for further use + * if the current process owns the parent directory + */ + if (securid) { + cached = fetch_cache(scx, dir_ni); + if (cached + && (cached->uid == scx->uid) + && (cached->gid == scx->gid)) { + if (fordir) + cached->inh_dirid = securid; + else + cached->inh_fileid = securid; + } + } + } + } + return (securid); +} + +/* + * Link a group to a member of group + * + * Returns 0 if OK, -1 (and errno set) if error + */ + +static int link_single_group(struct MAPPING *usermapping, struct passwd *user, + gid_t gid) +{ + struct group *group; + char **grmem; + int grcnt; + gid_t *groups; + int res; + + res = 0; + group = getgrgid(gid); + if (group && group->gr_mem) { + grcnt = usermapping->grcnt; + groups = usermapping->groups; + grmem = group->gr_mem; + while (*grmem && strcmp(user->pw_name, *grmem)) + grmem++; + if (*grmem) { + if (!grcnt) + groups = (gid_t*)malloc(sizeof(gid_t)); + else + groups = (gid_t*)realloc(groups, + (grcnt+1)*sizeof(gid_t)); + if (groups) + groups[grcnt++] = gid; + else { + res = -1; + errno = ENOMEM; + } + } + usermapping->grcnt = grcnt; + usermapping->groups = groups; + } + return (res); +} + + +/* + * Statically link group to users + * This is based on groups defined in /etc/group and does not take + * the groups dynamically set by setgroups() nor any changes in + * /etc/group into account + * + * Only mapped groups and root group are linked to mapped users + * + * Returns 0 if OK, -1 (and errno set) if error + * + */ + +static int link_group_members(struct SECURITY_CONTEXT *scx) +{ + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + struct passwd *user; + int res; + + res = 0; + for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res; + usermapping=usermapping->next) { + usermapping->grcnt = 0; + usermapping->groups = (gid_t*)NULL; + user = getpwuid(usermapping->xid); + if (user && user->pw_name) { + for (groupmapping=scx->mapping[MAPGROUPS]; + groupmapping && !res; + groupmapping=groupmapping->next) { + if (link_single_group(usermapping, user, + groupmapping->xid)) + res = -1; + } + if (!res && link_single_group(usermapping, + user, (gid_t)0)) + res = -1; + } + } + return (res); +} + +/* + * Apply default single user mapping + * returns zero if successful + */ + +static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx, + uid_t uid, gid_t gid, const SID *usid) +{ + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + SID *sid; + int sidsz; + int res; + + res = -1; + sidsz = ntfs_sid_size(usid); + sid = (SID*)ntfs_malloc(sidsz); + if (sid) { + memcpy(sid,usid,sidsz); + usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); + if (usermapping) { + groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); + if (groupmapping) { + usermapping->sid = sid; + usermapping->xid = uid; + usermapping->next = (struct MAPPING*)NULL; + groupmapping->sid = sid; + groupmapping->xid = gid; + groupmapping->next = (struct MAPPING*)NULL; + scx->mapping[MAPUSERS] = usermapping; + scx->mapping[MAPGROUPS] = groupmapping; + res = 0; + } + } + } + return (res); +} + +/* + * Make sure there are no ambiguous mapping + * Ambiguous mapping may lead to undesired configurations and + * we had rather be safe until the consequences are understood + */ + +#if 0 /* not activated for now */ + +static BOOL check_mapping(const struct MAPPING *usermapping, + const struct MAPPING *groupmapping) +{ + const struct MAPPING *mapping1; + const struct MAPPING *mapping2; + BOOL ambiguous; + + ambiguous = FALSE; + for (mapping1=usermapping; mapping1; mapping1=mapping1->next) + for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next) + if (ntfs_same_sid(mapping1->sid,mapping2->sid)) { + if (mapping1->xid != mapping2->xid) + ambiguous = TRUE; + } else { + if (mapping1->xid == mapping2->xid) + ambiguous = TRUE; + } + for (mapping1=groupmapping; mapping1; mapping1=mapping1->next) + for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next) + if (ntfs_same_sid(mapping1->sid,mapping2->sid)) { + if (mapping1->xid != mapping2->xid) + ambiguous = TRUE; + } else { + if (mapping1->xid == mapping2->xid) + ambiguous = TRUE; + } + return (ambiguous); +} + +#endif + +#if 0 /* not used any more */ + +/* + * Try and apply default single user mapping + * returns zero if successful + */ + +static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + ntfs_inode *ni; + char *securattr; + const SID *usid; + int res; + + res = -1; + ni = ntfs_pathname_to_inode(scx->vol, NULL, "/."); + if (ni) { + securattr = getsecurityattr(scx->vol, ni); + if (securattr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; + usid = (SID*)&securattr[le32_to_cpu(phead->owner)]; + if (ntfs_is_user_sid(usid)) + res = ntfs_do_default_mapping(scx, + scx->uid, scx->gid, usid); + free(securattr); + } + ntfs_inode_close(ni); + } + return (res); +} + +#endif + +/* + * Basic read from a user mapping file on another volume + */ + +static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused))) +{ + return (read(*(int*)fileid, buf, size)); +} + + +/* + * Read from a user mapping file on current NTFS partition + */ + +static int localread(void *fileid, char *buf, size_t size, off_t offs) +{ + return (ntfs_attr_data_read((ntfs_inode*)fileid, + AT_UNNAMED, 0, buf, size, offs)); +} + +/* + * Build the user mapping + * - according to a mapping file if defined (or default present), + * - or try default single user mapping if possible + * + * The mapping is specific to a mounted device + * No locking done, mounting assumed non multithreaded + * + * returns zero if mapping is successful + * (failure should not be interpreted as an error) + */ + +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef) +{ + struct MAPLIST *item; + struct MAPLIST *firstitem; + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + ntfs_inode *ni; + int fd; + static struct { + u8 revision; + u8 levels; + be16 highbase; + be32 lowbase; + le32 level1; + le32 level2; + le32 level3; + le32 level4; + le32 level5; + } defmap = { + 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5), + const_cpu_to_le32(21), + const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2), + const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE) + } ; + + /* be sure not to map anything until done */ + scx->mapping[MAPUSERS] = (struct MAPPING*)NULL; + scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL; + + if (!usermap_path) usermap_path = MAPPINGFILE; + if (usermap_path[0] == '/') { + fd = open(usermap_path,O_RDONLY); + if (fd > 0) { + firstitem = ntfs_read_mapping(basicread, (void*)&fd); + close(fd); + } else + firstitem = (struct MAPLIST*)NULL; + } else { + ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path); + if (ni) { + firstitem = ntfs_read_mapping(localread, ni); + ntfs_inode_close(ni); + } else + firstitem = (struct MAPLIST*)NULL; + } + + + if (firstitem) { + usermapping = ntfs_do_user_mapping(firstitem); + groupmapping = ntfs_do_group_mapping(firstitem); + if (usermapping && groupmapping) { + scx->mapping[MAPUSERS] = usermapping; + scx->mapping[MAPGROUPS] = groupmapping; + } else + ntfs_log_error("There were no valid user or no valid group\n"); + /* now we can free the memory copy of input text */ + /* and rely on internal representation */ + while (firstitem) { + item = firstitem->next; + free(firstitem); + firstitem = item; + } + } else { + /* no mapping file, try a default mapping */ + if (allowdef) { + if (!ntfs_do_default_mapping(scx, + 0, 0, (const SID*)&defmap)) + ntfs_log_info("Using default user mapping\n"); + } + } + return (!scx->mapping[MAPUSERS] || link_group_members(scx)); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get the ntfs attribute into an extended attribute + * The attribute is returned according to cpu endianness + */ + +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size) +{ + u32 attrib; + size_t outsize; + + outsize = 0; /* default to no data and no error */ + if (ni) { + attrib = le32_to_cpu(ni->flags); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY); + else + attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY); + if (!attrib) + attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL); + outsize = sizeof(FILE_ATTR_FLAGS); + if (size >= outsize) { + if (value) + memcpy(value,&attrib,outsize); + else + errno = EINVAL; + } + } + return (outsize ? (int)outsize : -errno); +} + +/* + * Return the ntfs attribute into an extended attribute + * The attribute is expected according to cpu endianness + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + u32 attrib; + le32 settable; + ATTR_FLAGS dirflags; + int res; + + res = -1; + if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) { + if (!(flags & XATTR_CREATE)) { + /* copy to avoid alignment problems */ + memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS)); + settable = FILE_ATTR_SETTABLE; + res = 0; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + /* + * Accept changing compression for a directory + * and set index root accordingly + */ + settable |= FILE_ATTR_COMPRESSED; + if ((ni->flags ^ cpu_to_le32(attrib)) + & FILE_ATTR_COMPRESSED) { + if (ni->flags & FILE_ATTR_COMPRESSED) + dirflags = const_cpu_to_le16(0); + else + dirflags = ATTR_IS_COMPRESSED; + res = ntfs_attr_set_flags(ni, + AT_INDEX_ROOT, + NTFS_INDEX_I30, 4, + dirflags, + ATTR_COMPRESSION_MASK); + } + } + if (!res) { + ni->flags = (ni->flags & ~settable) + | (cpu_to_le32(attrib) & settable); + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); + } + } else + errno = EEXIST; + } else + errno = EINVAL; + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Open $Secure once for all + * returns zero if it succeeds + * non-zero if it fails. This is not an error (on NTFS v1.x) + */ + + +int ntfs_open_secure(ntfs_volume *vol) +{ + ntfs_inode *ni; + int res; + + res = -1; + vol->secure_ni = (ntfs_inode*)NULL; + vol->secure_xsii = (ntfs_index_context*)NULL; + vol->secure_xsdh = (ntfs_index_context*)NULL; + if (vol->major_ver >= 3) { + /* make sure this is a genuine $Secure inode 9 */ + ni = ntfs_pathname_to_inode(vol, NULL, "$Secure"); + if (ni && (ni->mft_no == 9)) { + vol->secure_reentry = 0; + vol->secure_xsii = ntfs_index_ctx_get(ni, + sii_stream, 4); + vol->secure_xsdh = ntfs_index_ctx_get(ni, + sdh_stream, 4); + if (ni && vol->secure_xsii && vol->secure_xsdh) { + vol->secure_ni = ni; + res = 0; + } + } + } + return (res); +} + +/* + * Final cleaning + * Allocated memory is freed to facilitate the detection of memory leaks + */ + +void ntfs_close_secure(struct SECURITY_CONTEXT *scx) +{ + ntfs_volume *vol; + + vol = scx->vol; + if (vol->secure_ni) { + ntfs_index_ctx_put(vol->secure_xsii); + ntfs_index_ctx_put(vol->secure_xsdh); + ntfs_inode_close(vol->secure_ni); + + } + ntfs_free_mapping(scx->mapping); + free_caches(scx); +} + +/* + * API for direct access to security descriptors + * based on Win32 API + */ + + +/* + * Selective feeding of a security descriptor into user buffer + * + * Returns TRUE if successful + */ + +static BOOL feedsecurityattr(const char *attr, u32 selection, + char *buf, u32 buflen, u32 *psize) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + const ACL *pdacl; + const ACL *psacl; + const SID *pusid; + const SID *pgsid; + unsigned int offdacl; + unsigned int offsacl; + unsigned int offowner; + unsigned int offgroup; + unsigned int daclsz; + unsigned int saclsz; + unsigned int usidsz; + unsigned int gsidsz; + unsigned int size; /* size of requested attributes */ + BOOL ok; + unsigned int pos; + unsigned int avail; + le16 control; + + avail = 0; + control = SE_SELF_RELATIVE; + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + size = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + + /* locate DACL if requested and available */ + if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) { + offdacl = le32_to_cpu(phead->dacl); + pdacl = (const ACL*)&attr[offdacl]; + daclsz = le16_to_cpu(pdacl->size); + size += daclsz; + avail |= DACL_SECURITY_INFORMATION; + } else + offdacl = daclsz = 0; + + /* locate owner if requested and available */ + offowner = le32_to_cpu(phead->owner); + if (offowner && (selection & OWNER_SECURITY_INFORMATION)) { + /* find end of USID */ + pusid = (const SID*)&attr[offowner]; + usidsz = ntfs_sid_size(pusid); + size += usidsz; + avail |= OWNER_SECURITY_INFORMATION; + } else + offowner = usidsz = 0; + + /* locate group if requested and available */ + offgroup = le32_to_cpu(phead->group); + if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) { + /* find end of GSID */ + pgsid = (const SID*)&attr[offgroup]; + gsidsz = ntfs_sid_size(pgsid); + size += gsidsz; + avail |= GROUP_SECURITY_INFORMATION; + } else + offgroup = gsidsz = 0; + + /* locate SACL if requested and available */ + if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) { + /* find end of SACL */ + offsacl = le32_to_cpu(phead->sacl); + psacl = (const ACL*)&attr[offsacl]; + saclsz = le16_to_cpu(psacl->size); + size += saclsz; + avail |= SACL_SECURITY_INFORMATION; + } else + offsacl = saclsz = 0; + + /* + * Check having enough size in destination buffer + * (required size is returned nevertheless so that + * the request can be reissued with adequate size) + */ + if (size > buflen) { + *psize = size; + errno = EINVAL; + ok = FALSE; + } else { + if (selection & OWNER_SECURITY_INFORMATION) + control |= phead->control & SE_OWNER_DEFAULTED; + if (selection & GROUP_SECURITY_INFORMATION) + control |= phead->control & SE_GROUP_DEFAULTED; + if (selection & DACL_SECURITY_INFORMATION) + control |= phead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_AUTO_INHERITED + | SE_DACL_PROTECTED); + if (selection & SACL_SECURITY_INFORMATION) + control |= phead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_AUTO_INHERITED + | SE_SACL_PROTECTED); + /* + * copy header and feed new flags, even if no detailed data + */ + memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf; + pnhead->control = control; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + + /* copy DACL if requested and available */ + if (selection & avail & DACL_SECURITY_INFORMATION) { + pnhead->dacl = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offdacl],daclsz); + pos += daclsz; + } else + pnhead->dacl = const_cpu_to_le32(0); + + /* copy SACL if requested and available */ + if (selection & avail & SACL_SECURITY_INFORMATION) { + pnhead->sacl = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offsacl],saclsz); + pos += saclsz; + } else + pnhead->sacl = const_cpu_to_le32(0); + + /* copy owner if requested and available */ + if (selection & avail & OWNER_SECURITY_INFORMATION) { + pnhead->owner = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offowner],usidsz); + pos += usidsz; + } else + pnhead->owner = const_cpu_to_le32(0); + + /* copy group if requested and available */ + if (selection & avail & GROUP_SECURITY_INFORMATION) { + pnhead->group = cpu_to_le32(pos); + memcpy(&buf[pos],&attr[offgroup],gsidsz); + pos += gsidsz; + } else + pnhead->group = const_cpu_to_le32(0); + if (pos != size) + ntfs_log_error("Error in security descriptor size\n"); + *psize = size; + ok = TRUE; + } + + return (ok); +} + +/* + * Merge a new security descriptor into the old one + * and assign to designated file + * + * Returns TRUE if successful + */ + +static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr, + const char *newattr, u32 selection, ntfs_inode *ni) +{ + const SECURITY_DESCRIPTOR_RELATIVE *oldhead; + const SECURITY_DESCRIPTOR_RELATIVE *newhead; + SECURITY_DESCRIPTOR_RELATIVE *targhead; + const ACL *pdacl; + const ACL *psacl; + const SID *powner; + const SID *pgroup; + int offdacl; + int offsacl; + int offowner; + int offgroup; + unsigned int size; + le16 control; + char *target; + int pos; + int oldattrsz; + int newattrsz; + BOOL ok; + + ok = FALSE; /* default return */ + oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; + newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr; + oldattrsz = ntfs_attr_size(oldattr); + newattrsz = ntfs_attr_size(newattr); + target = (char*)ntfs_malloc(oldattrsz + newattrsz); + if (target) { + targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target; + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + control = SE_SELF_RELATIVE; + /* + * copy new DACL if selected + * or keep old DACL if any + */ + if ((selection & DACL_SECURITY_INFORMATION) ? + newhead->dacl : oldhead->dacl) { + if (selection & DACL_SECURITY_INFORMATION) { + offdacl = le32_to_cpu(newhead->dacl); + pdacl = (const ACL*)&newattr[offdacl]; + } else { + offdacl = le32_to_cpu(oldhead->dacl); + pdacl = (const ACL*)&oldattr[offdacl]; + } + size = le16_to_cpu(pdacl->size); + memcpy(&target[pos], pdacl, size); + targhead->dacl = cpu_to_le32(pos); + pos += size; + } else + targhead->dacl = const_cpu_to_le32(0); + if (selection & DACL_SECURITY_INFORMATION) { + control |= newhead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_PROTECTED); + if (newhead->control & SE_DACL_AUTO_INHERIT_REQ) + control |= SE_DACL_AUTO_INHERITED; + } else + control |= oldhead->control + & (SE_DACL_PRESENT + | SE_DACL_DEFAULTED + | SE_DACL_AUTO_INHERITED + | SE_DACL_PROTECTED); + /* + * copy new SACL if selected + * or keep old SACL if any + */ + if ((selection & SACL_SECURITY_INFORMATION) ? + newhead->sacl : oldhead->sacl) { + if (selection & SACL_SECURITY_INFORMATION) { + offsacl = le32_to_cpu(newhead->sacl); + psacl = (const ACL*)&newattr[offsacl]; + } else { + offsacl = le32_to_cpu(oldhead->sacl); + psacl = (const ACL*)&oldattr[offsacl]; + } + size = le16_to_cpu(psacl->size); + memcpy(&target[pos], psacl, size); + targhead->sacl = cpu_to_le32(pos); + pos += size; + } else + targhead->sacl = const_cpu_to_le32(0); + if (selection & SACL_SECURITY_INFORMATION) { + control |= newhead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_PROTECTED); + if (newhead->control & SE_SACL_AUTO_INHERIT_REQ) + control |= SE_SACL_AUTO_INHERITED; + } else + control |= oldhead->control + & (SE_SACL_PRESENT + | SE_SACL_DEFAULTED + | SE_SACL_AUTO_INHERITED + | SE_SACL_PROTECTED); + /* + * copy new OWNER if selected + * or keep old OWNER if any + */ + if ((selection & OWNER_SECURITY_INFORMATION) ? + newhead->owner : oldhead->owner) { + if (selection & OWNER_SECURITY_INFORMATION) { + offowner = le32_to_cpu(newhead->owner); + powner = (const SID*)&newattr[offowner]; + } else { + offowner = le32_to_cpu(oldhead->owner); + powner = (const SID*)&oldattr[offowner]; + } + size = ntfs_sid_size(powner); + memcpy(&target[pos], powner, size); + targhead->owner = cpu_to_le32(pos); + pos += size; + } else + targhead->owner = const_cpu_to_le32(0); + if (selection & OWNER_SECURITY_INFORMATION) + control |= newhead->control & SE_OWNER_DEFAULTED; + else + control |= oldhead->control & SE_OWNER_DEFAULTED; + /* + * copy new GROUP if selected + * or keep old GROUP if any + */ + if ((selection & GROUP_SECURITY_INFORMATION) ? + newhead->group : oldhead->group) { + if (selection & GROUP_SECURITY_INFORMATION) { + offgroup = le32_to_cpu(newhead->group); + pgroup = (const SID*)&newattr[offgroup]; + control |= newhead->control + & SE_GROUP_DEFAULTED; + } else { + offgroup = le32_to_cpu(oldhead->group); + pgroup = (const SID*)&oldattr[offgroup]; + control |= oldhead->control + & SE_GROUP_DEFAULTED; + } + size = ntfs_sid_size(pgroup); + memcpy(&target[pos], pgroup, size); + targhead->group = cpu_to_le32(pos); + pos += size; + } else + targhead->group = const_cpu_to_le32(0); + if (selection & GROUP_SECURITY_INFORMATION) + control |= newhead->control & SE_GROUP_DEFAULTED; + else + control |= oldhead->control & SE_GROUP_DEFAULTED; + targhead->revision = SECURITY_DESCRIPTOR_REVISION; + targhead->alignment = 0; + targhead->control = control; + ok = !update_secur_descr(vol, target, ni); + free(target); + } + return (ok); +} + +/* + * Return the security descriptor of a file + * This is intended to be similar to GetFileSecurity() from Win32 + * in order to facilitate the development of portable tools + * + * returns zero if unsuccessful (following Win32 conventions) + * -1 if no securid + * the securid if any + * + * The Win32 API is : + * + * BOOL WINAPI GetFileSecurity( + * __in LPCTSTR lpFileName, + * __in SECURITY_INFORMATION RequestedInformation, + * __out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor, + * __in DWORD nLength, + * __out LPDWORD lpnLengthNeeded + * ); + * + */ + +int ntfs_get_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, + char *buf, u32 buflen, u32 *psize) +{ + ntfs_inode *ni; + char *attr; + int res; + + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + attr = getsecurityattr(scapi->security.vol, ni); + if (attr) { + if (feedsecurityattr(attr,selection, + buf,buflen,psize)) { + if (test_nino_flag(ni, v3_Extensions) + && ni->security_id) + res = le32_to_cpu( + ni->security_id); + else + res = -1; + } + free(attr); + } + ntfs_inode_close(ni); + } else + errno = ENOENT; + if (!res) *psize = 0; + } else + errno = EINVAL; /* do not clear *psize */ + return (res); +} + + +/* + * Set the security descriptor of a file or directory + * This is intended to be similar to SetFileSecurity() from Win32 + * in order to facilitate the development of portable tools + * + * returns zero if unsuccessful (following Win32 conventions) + * -1 if no securid + * the securid if any + * + * The Win32 API is : + * + * BOOL WINAPI SetFileSecurity( + * __in LPCTSTR lpFileName, + * __in SECURITY_INFORMATION SecurityInformation, + * __in PSECURITY_DESCRIPTOR pSecurityDescriptor + * ); + */ + +int ntfs_set_file_security(struct SECURITY_API *scapi, + const char *path, u32 selection, const char *attr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + ntfs_inode *ni; + int attrsz; + BOOL missing; + char *oldattr; + int res; + + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && attr) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + attrsz = ntfs_attr_size(attr); + /* if selected, owner and group must be present or defaulted */ + missing = ((selection & OWNER_SECURITY_INFORMATION) + && !phead->owner + && !(phead->control & SE_OWNER_DEFAULTED)) + || ((selection & GROUP_SECURITY_INFORMATION) + && !phead->group + && !(phead->control & SE_GROUP_DEFAULTED)); + if (!missing + && (phead->control & SE_SELF_RELATIVE) + && ntfs_valid_descr(attr, attrsz)) { + ni = ntfs_pathname_to_inode(scapi->security.vol, + NULL, path); + if (ni) { + oldattr = getsecurityattr(scapi->security.vol, + ni); + if (oldattr) { + if (mergesecurityattr( + scapi->security.vol, + oldattr, attr, + selection, ni)) { + if (test_nino_flag(ni, + v3_Extensions)) + res = le32_to_cpu( + ni->security_id); + else + res = -1; + } + free(oldattr); + } + ntfs_inode_close(ni); + } + } else + errno = EINVAL; + } else + errno = EINVAL; + return (res); +} + + +/* + * Return the attributes of a file + * This is intended to be similar to GetFileAttributes() from Win32 + * in order to facilitate the development of portable tools + * + * returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES) + * + * The Win32 API is : + * + * DWORD WINAPI GetFileAttributes( + * __in LPCTSTR lpFileName + * ); + */ + +int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path) +{ + ntfs_inode *ni; + s32 attrib; + + attrib = -1; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && path) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + attrib = le32_to_cpu(ni->flags); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY); + else + attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY); + if (!attrib) + attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL); + + ntfs_inode_close(ni); + } else + errno = ENOENT; + } else + errno = EINVAL; /* do not clear *psize */ + return (attrib); +} + + +/* + * Set attributes to a file or directory + * This is intended to be similar to SetFileAttributes() from Win32 + * in order to facilitate the development of portable tools + * + * Only a few flags can be set (same list as Win32) + * + * returns zero if unsuccessful (following Win32 conventions) + * nonzero if successful + * + * The Win32 API is : + * + * BOOL WINAPI SetFileAttributes( + * __in LPCTSTR lpFileName, + * __in DWORD dwFileAttributes + * ); + */ + +BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, + const char *path, s32 attrib) +{ + ntfs_inode *ni; + le32 settable; + ATTR_FLAGS dirflags; + int res; + + res = 0; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && path) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + settable = FILE_ATTR_SETTABLE; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + /* + * Accept changing compression for a directory + * and set index root accordingly + */ + settable |= FILE_ATTR_COMPRESSED; + if ((ni->flags ^ cpu_to_le32(attrib)) + & FILE_ATTR_COMPRESSED) { + if (ni->flags & FILE_ATTR_COMPRESSED) + dirflags = const_cpu_to_le16(0); + else + dirflags = ATTR_IS_COMPRESSED; + res = ntfs_attr_set_flags(ni, + AT_INDEX_ROOT, + NTFS_INDEX_I30, 4, + dirflags, + ATTR_COMPRESSION_MASK); + } + } + if (!res) { + ni->flags = (ni->flags & ~settable) + | (cpu_to_le32(attrib) & settable); + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + } + if (!ntfs_inode_close(ni)) + res = -1; + } else + errno = ENOENT; + } + return (res); +} + + +BOOL ntfs_read_directory(struct SECURITY_API *scapi, + const char *path, ntfs_filldir_t callback, void *context) +{ + ntfs_inode *ni; + BOOL ok; + s64 pos; + + ok = FALSE; /* default return */ + if (scapi && (scapi->magic == MAGIC_API) && callback) { + ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); + if (ni) { + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + pos = 0; + ntfs_readdir(ni,&pos,context,callback); + ok = !ntfs_inode_close(ni); + } else { + ntfs_inode_close(ni); + errno = ENOTDIR; + } + } else + errno = ENOENT; + } else + errno = EINVAL; /* do not clear *psize */ + return (ok); +} + +/* + * read $SDS (for auditing security data) + * + * Returns the number or read bytes, or -1 if there is an error + */ + +int ntfs_read_sds(struct SECURITY_API *scapi, + char *buf, u32 size, u32 offset) +{ + int got; + + got = -1; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + if (scapi->security.vol->secure_ni) + got = ntfs_attr_data_read(scapi->security.vol->secure_ni, + STREAM_SDS, 4, buf, size, offset); + else + errno = EOPNOTSUPP; + } else + errno = EINVAL; + return (got); +} + +/* + * read $SII (for auditing security data) + * + * Returns next entry, or NULL if there is an error + */ + +INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi, + INDEX_ENTRY *entry) +{ + SII_INDEX_KEY key; + INDEX_ENTRY *ret; + BOOL found; + ntfs_index_context *xsii; + + ret = (INDEX_ENTRY*)NULL; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + xsii = scapi->security.vol->secure_xsii; + if (xsii) { + if (!entry) { + key.security_id = const_cpu_to_le32(0); + found = !ntfs_index_lookup((char*)&key, + sizeof(SII_INDEX_KEY), xsii); + /* not supposed to find */ + if (!found && (errno == ENOENT)) + ret = xsii->entry; + } else + ret = ntfs_index_next(entry,xsii); + if (!ret) + errno = ENODATA; + } else + errno = EOPNOTSUPP; + } else + errno = EINVAL; + return (ret); +} + +/* + * read $SDH (for auditing security data) + * + * Returns next entry, or NULL if there is an error + */ + +INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi, + INDEX_ENTRY *entry) +{ + SDH_INDEX_KEY key; + INDEX_ENTRY *ret; + BOOL found; + ntfs_index_context *xsdh; + + ret = (INDEX_ENTRY*)NULL; /* default return */ + if (scapi && (scapi->magic == MAGIC_API)) { + xsdh = scapi->security.vol->secure_xsdh; + if (xsdh) { + if (!entry) { + key.hash = const_cpu_to_le32(0); + key.security_id = const_cpu_to_le32(0); + found = !ntfs_index_lookup((char*)&key, + sizeof(SDH_INDEX_KEY), xsdh); + /* not supposed to find */ + if (!found && (errno == ENOENT)) + ret = xsdh->entry; + } else + ret = ntfs_index_next(entry,xsdh); + if (!ret) + errno = ENODATA; + } else errno = ENOTSUP; + } else + errno = EINVAL; + return (ret); +} + +/* + * Get the mapped user SID + * A buffer of 40 bytes has to be supplied + * + * returns the size of the SID, or zero and errno set if not found + */ + +int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf) +{ + const SID *usid; + BIGSID defusid; + int size; + + size = 0; + if (scapi && (scapi->magic == MAGIC_API)) { + usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid); + if (usid) { + size = ntfs_sid_size(usid); + memcpy(buf,usid,size); + } else + errno = ENODATA; + } else + errno = EINVAL; + return (size); +} + +/* + * Get the mapped group SID + * A buffer of 40 bytes has to be supplied + * + * returns the size of the SID, or zero and errno set if not found + */ + +int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf) +{ + const SID *gsid; + BIGSID defgsid; + int size; + + size = 0; + if (scapi && (scapi->magic == MAGIC_API)) { + gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid); + if (gsid) { + size = ntfs_sid_size(gsid); + memcpy(buf,gsid,size); + } else + errno = ENODATA; + } else + errno = EINVAL; + return (size); +} + +/* + * Get the user mapped to a SID + * + * returns the uid, or -1 if not found + */ + +int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid) +{ + int uid; + + uid = -1; + if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) { + if (ntfs_same_sid(usid,adminsid)) + uid = 0; + else { + uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid); + if (!uid) { + uid = -1; + errno = ENODATA; + } + } + } else + errno = EINVAL; + return (uid); +} + +/* + * Get the group mapped to a SID + * + * returns the uid, or -1 if not found + */ + +int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid) +{ + int gid; + + gid = -1; + if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) { + if (ntfs_same_sid(gsid,adminsid)) + gid = 0; + else { + gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid); + if (!gid) { + gid = -1; + errno = ENODATA; + } + } + } else + errno = EINVAL; + return (gid); +} + +/* + * Initializations before calling ntfs_get_file_security() + * ntfs_set_file_security() and ntfs_read_directory() + * + * Only allowed for root + * + * Returns an (obscured) struct SECURITY_API* needed for further calls + * NULL if not root (EPERM) or device is mounted (EBUSY) + */ + +struct SECURITY_API *ntfs_initialize_file_security(const char *device, + unsigned long flags) +{ + ntfs_volume *vol; + unsigned long mntflag; + int mnt; + struct SECURITY_API *scapi; + struct SECURITY_CONTEXT *scx; + + scapi = (struct SECURITY_API*)NULL; + mnt = ntfs_check_if_mounted(device, &mntflag); + if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) { + vol = ntfs_mount(device, flags); + if (vol) { + scapi = (struct SECURITY_API*) + ntfs_malloc(sizeof(struct SECURITY_API)); + if (!ntfs_volume_get_free_space(vol) + && scapi) { + scapi->magic = MAGIC_API; + scapi->seccache = (struct PERMISSIONS_CACHE*)NULL; + scx = &scapi->security; + scx->vol = vol; + scx->uid = getuid(); + scx->gid = getgid(); + scx->pseccache = &scapi->seccache; + scx->vol->secure_flags = 0; + /* accept no mapping and no $Secure */ + ntfs_build_mapping(scx,(const char*)NULL,TRUE); + ntfs_open_secure(vol); + } else { + if (scapi) + free(scapi); + else + errno = ENOMEM; + mnt = ntfs_umount(vol,FALSE); + scapi = (struct SECURITY_API*)NULL; + } + } + } else + if (getuid()) + errno = EPERM; + else + errno = EBUSY; + return (scapi); +} + +/* + * Leaving after ntfs_initialize_file_security() + * + * Returns FALSE if FAILED + */ + +BOOL ntfs_leave_file_security(struct SECURITY_API *scapi) +{ + int ok; + ntfs_volume *vol; + + ok = FALSE; + if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) { + vol = scapi->security.vol; + ntfs_close_secure(&scapi->security); + free(scapi); + if (!ntfs_umount(vol, 0)) + ok = TRUE; + } + return (ok); +} + diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c new file mode 100755 index 0000000000000000000000000000000000000000..e12d21e75dd2117725d8f9781e8395f8c5afba72 --- /dev/null +++ b/libntfs-3g/unistr.c @@ -0,0 +1,1609 @@ +/** + * unistr.c - Unicode string handling. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2002-2009 Szabolcs Szakacsits + * Copyright (c) 2008-2014 Jean-Pierre Andre + * Copyright (c) 2008 Bernhard Kaindl + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_WCHAR_H +#include <wchar.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif + +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV +#include <CoreFoundation/CoreFoundation.h> +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + +#include "compat.h" +#include "attrib.h" +#include "types.h" +#include "unistr.h" +#include "debug.h" +#include "logging.h" +#include "misc.h" + +#define NOREVBOM 0 /* JPA rejecting U+FFFE and U+FFFF, open to debate */ + +/* + * IMPORTANT + * ========= + * + * All these routines assume that the Unicode characters are in little endian + * encoding inside the strings!!! + */ + +static int use_utf8 = 1; /* use UTF-8 encoding for file names */ + +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV +/** + * This variable controls whether or not automatic normalization form conversion + * should be performed when translating NTFS unicode file names to UTF-8. + * Defaults to on, but can be controlled from the outside using the function + * int ntfs_macosx_normalize_filenames(int normalize); + */ +static int nfconvert_utf8 = 1; +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + +/* + * This is used by the name collation functions to quickly determine what + * characters are (in)valid. + */ +#if 0 +static const u8 legal_ansi_char_array[0x40] = { + 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + + 0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00, + + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18, +}; +#endif + +/** + * ntfs_names_are_equal - compare two Unicode names for equality + * @s1: name to compare to @s2 + * @s1_len: length in Unicode characters of @s1 + * @s2: name to compare to @s1 + * @s2_len: length in Unicode characters of @s2 + * @ic: ignore case bool + * @upcase: upcase table (only if @ic == IGNORE_CASE) + * @upcase_size: length in Unicode characters of @upcase (if present) + * + * Compare the names @s1 and @s2 and return TRUE (1) if the names are + * identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE, + * the @upcase table is used to perform a case insensitive comparison. + */ +BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, + const ntfschar *s2, size_t s2_len, + const IGNORE_CASE_BOOL ic, + const ntfschar *upcase, const u32 upcase_size) +{ + if (s1_len != s2_len) + return FALSE; + if (!s1_len) + return TRUE; + if (ic == CASE_SENSITIVE) + return ntfs_ucsncmp(s1, s2, s1_len) ? FALSE: TRUE; + return ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? FALSE: + TRUE; +} + +/* + * ntfs_names_full_collate() fully collate two Unicode names + * + * @name1: first Unicode name to compare + * @name1_len: length of first Unicode name to compare + * @name2: second Unicode name to compare + * @name2_len: length of second Unicode name to compare + * @ic: either CASE_SENSITIVE or IGNORE_CASE + * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) + * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) + * + * -1 if the first name collates before the second one, + * 0 if the names match, + * 1 if the second name collates before the first one, or + * + */ +int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, + const ntfschar *name2, const u32 name2_len, + const IGNORE_CASE_BOOL ic, const ntfschar *upcase, + const u32 upcase_len) +{ + u32 cnt; + u16 c1, c2; + u16 u1, u2; + +#ifdef DEBUG + if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { + ntfs_log_debug("ntfs_names_collate received NULL pointer!\n"); + exit(1); + } +#endif + cnt = min(name1_len, name2_len); + if (cnt > 0) { + if (ic == CASE_SENSITIVE) { + while (--cnt && (*name1 == *name2)) { + name1++; + name2++; + } + u1 = c1 = le16_to_cpu(*name1); + u2 = c2 = le16_to_cpu(*name2); + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + if ((u1 == u2) && cnt) + do { + name1++; + u1 = le16_to_cpu(*name1); + name2++; + u2 = le16_to_cpu(*name2); + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } else { + do { + u1 = c1 = le16_to_cpu(*name1); + name1++; + u2 = c2 = le16_to_cpu(*name2); + name2++; + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + } + } else { + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + } + return 0; +} + +/** + * ntfs_ucsncmp - compare two little endian Unicode strings + * @s1: first string + * @s2: second string + * @n: maximum unicode characters to compare + * + * Compare the first @n characters of the Unicode strings @s1 and @s2, + * The strings in little endian format and appropriate le16_to_cpu() + * conversion is performed on non-little endian machines. + * + * The function returns an integer less than, equal to, or greater than zero + * if @s1 (or the first @n Unicode characters thereof) is found, respectively, + * to be less than, to match, or be greater than @s2. + */ +int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) +{ + ntfschar c1, c2; + size_t i; + +#ifdef DEBUG + if (!s1 || !s2) { + ntfs_log_debug("ntfs_wcsncmp() received NULL pointer!\n"); + exit(1); + } +#endif + for (i = 0; i < n; ++i) { + c1 = le16_to_cpu(s1[i]); + c2 = le16_to_cpu(s2[i]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (!c1) + break; + } + return 0; +} + +/** + * ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case + * @s1: first string + * @s2: second string + * @n: maximum unicode characters to compare + * @upcase: upcase table + * @upcase_size: upcase table size in Unicode characters + * + * Compare the first @n characters of the Unicode strings @s1 and @s2, + * ignoring case. The strings in little endian format and appropriate + * le16_to_cpu() conversion is performed on non-little endian machines. + * + * Each character is uppercased using the @upcase table before the comparison. + * + * The function returns an integer less than, equal to, or greater than zero + * if @s1 (or the first @n Unicode characters thereof) is found, respectively, + * to be less than, to match, or be greater than @s2. + */ +int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, + const ntfschar *upcase, const u32 upcase_size) +{ + u16 c1, c2; + size_t i; + +#ifdef DEBUG + if (!s1 || !s2 || !upcase) { + ntfs_log_debug("ntfs_wcsncasecmp() received NULL pointer!\n"); + exit(1); + } +#endif + for (i = 0; i < n; ++i) { + if ((c1 = le16_to_cpu(s1[i])) < upcase_size) + c1 = le16_to_cpu(upcase[c1]); + if ((c2 = le16_to_cpu(s2[i])) < upcase_size) + c2 = le16_to_cpu(upcase[c2]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + if (!c1) + break; + } + return 0; +} + +/** + * ntfs_ucsnlen - determine the length of a little endian Unicode string + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s + * + * Return the number of Unicode characters in the little endian Unicode + * string @s up to a maximum of maxlen Unicode characters, not including + * the terminating (ntfschar)'\0'. If there is no (ntfschar)'\0' between @s + * and @s + @maxlen, @maxlen is returned. + * + * This function never looks beyond @s + @maxlen. + */ +u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen) +{ + u32 i; + + for (i = 0; i < maxlen; i++) { + if (!le16_to_cpu(s[i])) + break; + } + return i; +} + +/** + * ntfs_ucsndup - duplicate little endian Unicode string + * @s: pointer to Unicode string + * @maxlen: maximum length of string @s + * + * Return a pointer to a new little endian Unicode string which is a duplicate + * of the string s. Memory for the new string is obtained with ntfs_malloc(3), + * and can be freed with free(3). + * + * A maximum of @maxlen Unicode characters are copied and a terminating + * (ntfschar)'\0' little endian Unicode character is added. + * + * This function never looks beyond @s + @maxlen. + * + * Return a pointer to the new little endian Unicode string on success and NULL + * on failure with errno set to the error code. + */ +ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen) +{ + ntfschar *dst; + u32 len; + + len = ntfs_ucsnlen(s, maxlen); + dst = ntfs_malloc((len + 1) * sizeof(ntfschar)); + if (dst) { + memcpy(dst, s, len * sizeof(ntfschar)); + dst[len] = cpu_to_le16(L'\0'); + } + return dst; +} + +/** + * ntfs_name_upcase - Map an Unicode name to its uppercase equivalent + * @name: + * @name_len: + * @upcase: + * @upcase_len: + * + * Description... + * + * Returns: + */ +void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase, + const u32 upcase_len) +{ + u32 i; + u16 u; + + for (i = 0; i < name_len; i++) + if ((u = le16_to_cpu(name[i])) < upcase_len) + name[i] = upcase[u]; +} + +/** + * ntfs_name_locase - Map a Unicode name to its lowercase equivalent + */ +void ntfs_name_locase(ntfschar *name, u32 name_len, const ntfschar *locase, + const u32 locase_len) +{ + u32 i; + u16 u; + + if (locase) + for (i = 0; i < name_len; i++) + if ((u = le16_to_cpu(name[i])) < locase_len) + name[i] = locase[u]; +} + +/** + * ntfs_file_value_upcase - Convert a filename to upper case + * @file_name_attr: + * @upcase: + * @upcase_len: + * + * Description... + * + * Returns: + */ +void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, + const ntfschar *upcase, const u32 upcase_len) +{ + ntfs_name_upcase((ntfschar*)&file_name_attr->file_name, + file_name_attr->file_name_length, upcase, upcase_len); +} + +/* + NTFS uses Unicode (UTF-16LE [NTFS-3G uses UCS-2LE, which is enough + for now]) for path names, but the Unicode code points need to be + converted before a path can be accessed under NTFS. For 7 bit ASCII/ANSI, + glibc does this even without a locale in a hard-coded fashion as that + appears to be is easy because the low 7-bit ASCII range appears to be + available in all charsets but it does not convert anything if + there was some error with the locale setup or none set up like + when mount is called during early boot where he (by policy) do + not use locales (and may be not available if /usr is not yet mounted), + so this patch fixes the resulting issues for systems which use + UTF-8 and for others, specifying the locale in fstab brings them + the encoding which they want. + + If no locale is defined or there was a problem with setting one + up and whenever nl_langinfo(CODESET) returns a sting starting with + "ANSI", use an internal UCS-2LE <-> UTF-8 codeset converter to fix + the bug where NTFS-3G does not show any path names which include + international characters!!! (and also fails on creating them) as result. + + Author: Bernhard Kaindl <bk@suse.de> + Jean-Pierre Andre made it compliant with RFC3629/RFC2781. +*/ + +/* + * Return the amount of 8-bit elements in UTF-8 needed (without the terminating + * null) to store a given UTF-16LE string. + * + * Return -1 with errno set if string has invalid byte sequence or too long. + */ +static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_len) +{ + int i, ret = -1; + int count = 0; + BOOL surrog; + + surrog = FALSE; + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + if (surrog) { + if ((c >= 0xdc00) && (c < 0xe000)) { + surrog = FALSE; + count += 4; + } else + goto fail; + } else + if (c < 0x80) + count++; + else if (c < 0x800) + count += 2; + else if (c < 0xd800) + count += 3; + else if (c < 0xdc00) + surrog = TRUE; +#if NOREVBOM + else if ((c >= 0xe000) && (c < 0xfffe)) +#else + else if (c >= 0xe000) +#endif + count += 3; + else + goto fail; + if (count > outs_len) { + errno = ENAMETOOLONG; + goto out; + } + } + if (surrog) + goto fail; + + ret = count; +out: + return ret; +fail: + errno = EILSEQ; + goto out; +} + +/* + * ntfs_utf16_to_utf8 - convert a little endian UTF16LE string to an UTF-8 string + * @ins: input utf16 string buffer + * @ins_len: length of input string in utf16 characters + * @outs: on return contains the (allocated) output multibyte string + * @outs_len: length of output buffer in bytes + * + * Return -1 with errno set if string has invalid byte sequence or too long. + */ +static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, + char **outs, int outs_len) +{ +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + char *original_outs_value = *outs; + int original_outs_len = outs_len; +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + + char *t; + int i, size, ret = -1; + int halfpair; + + halfpair = 0; + if (!*outs) + outs_len = PATH_MAX; + + size = utf16_to_utf8_size(ins, ins_len, outs_len); + + if (size < 0) + goto out; + + if (!*outs) { + outs_len = size + 1; + *outs = ntfs_malloc(outs_len); + if (!*outs) + goto out; + } + + t = *outs; + + for (i = 0; i < ins_len && ins[i]; i++) { + unsigned short c = le16_to_cpu(ins[i]); + /* size not double-checked */ + if (halfpair) { + if ((c >= 0xdc00) && (c < 0xe000)) { + *t++ = 0xf0 + (((halfpair + 64) >> 8) & 7); + *t++ = 0x80 + (((halfpair + 64) >> 2) & 63); + *t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4); + *t++ = 0x80 + (c & 63); + halfpair = 0; + } else + goto fail; + } else if (c < 0x80) { + *t++ = c; + } else { + if (c < 0x800) { + *t++ = (0xc0 | ((c >> 6) & 0x3f)); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xd800) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else if (c < 0xdc00) + halfpair = c; + else if (c >= 0xe000) { + *t++ = 0xe0 | (c >> 12); + *t++ = 0x80 | ((c >> 6) & 0x3f); + *t++ = 0x80 | (c & 0x3f); + } else + goto fail; + } + } + *t = '\0'; + +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + if(nfconvert_utf8 && (t - *outs) > 0) { + char *new_outs = NULL; + int new_outs_len = ntfs_macosx_normalize_utf8(*outs, &new_outs, 0); // Normalize to decomposed form + if(new_outs_len >= 0 && new_outs != NULL) { + if(original_outs_value != *outs) { + // We have allocated outs ourselves. + free(*outs); + *outs = new_outs; + t = *outs + new_outs_len; + } + else { + // We need to copy new_outs into the fixed outs buffer. + memset(*outs, 0, original_outs_len); + strncpy(*outs, new_outs, original_outs_len-1); + t = *outs + original_outs_len; + free(new_outs); + } + } + else { + ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFD: %s\n", *outs); + ntfs_log_error(" new_outs=0x%p\n", new_outs); + ntfs_log_error(" new_outs_len=%d\n", new_outs_len); + } + } +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + + ret = t - *outs; +out: + return ret; +fail: + errno = EILSEQ; + goto out; +} + +/* + * Return the amount of 16-bit elements in UTF-16LE needed + * (without the terminating null) to store given UTF-8 string. + * + * Return -1 with errno set if it's longer than PATH_MAX or string is invalid. + * + * Note: This does not check whether the input sequence is a valid utf8 string, + * and should be used only in context where such check is made! + */ +static int utf8_to_utf16_size(const char *s) +{ + int ret = -1; + unsigned int byte; + size_t count = 0; + + while ((byte = *((const unsigned char *)s++))) { + if (++count >= PATH_MAX) + goto fail; + if (byte >= 0xc0) { + if (byte >= 0xF5) { + errno = EILSEQ; + goto out; + } + if (!*s) + break; + if (byte >= 0xC0) + s++; + if (!*s) + break; + if (byte >= 0xE0) + s++; + if (!*s) + break; + if (byte >= 0xF0) { + s++; + if (++count >= PATH_MAX) + goto fail; + } + } + } + ret = count; +out: + return ret; +fail: + errno = ENAMETOOLONG; + goto out; +} +/* + * This converts one UTF-8 sequence to cpu-endian Unicode value + * within range U+0 .. U+10ffff and excluding U+D800 .. U+DFFF + * + * Return the number of used utf8 bytes or -1 with errno set + * if sequence is invalid. + */ +static int utf8_to_unicode(u32 *wc, const char *s) +{ + unsigned int byte = *((const unsigned char *)s); + + /* single byte */ + if (byte == 0) { + *wc = (u32) 0; + return 0; + } else if (byte < 0x80) { + *wc = (u32) byte; + return 1; + /* double byte */ + } else if (byte < 0xc2) { + goto fail; + } else if (byte < 0xE0) { + if ((s[1] & 0xC0) == 0x80) { + *wc = ((u32)(byte & 0x1F) << 6) + | ((u32)(s[1] & 0x3F)); + return 2; + } else + goto fail; + /* three-byte */ + } else if (byte < 0xF0) { + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x0F) << 12) + | ((u32)(s[1] & 0x3F) << 6) + | ((u32)(s[2] & 0x3F)); + /* Check valid ranges */ +#if NOREVBOM + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFD))) + return 3; +#else + if (((*wc >= 0x800) && (*wc <= 0xD7FF)) + || ((*wc >= 0xe000) && (*wc <= 0xFFFF))) + return 3; +#endif + } + goto fail; + /* four-byte */ + } else if (byte < 0xF5) { + if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80) + && ((s[3] & 0xC0) == 0x80)) { + *wc = ((u32)(byte & 0x07) << 18) + | ((u32)(s[1] & 0x3F) << 12) + | ((u32)(s[2] & 0x3F) << 6) + | ((u32)(s[3] & 0x3F)); + /* Check valid ranges */ + if ((*wc <= 0x10ffff) && (*wc >= 0x10000)) + return 4; + } + goto fail; + } +fail: + errno = EILSEQ; + return -1; +} + +/** + * ntfs_utf8_to_utf16 - convert a UTF-8 string to a UTF-16LE string + * @ins: input multibyte string buffer + * @outs: on return contains the (allocated) output utf16 string + * @outs_len: length of output buffer in utf16 characters + * + * Return -1 with errno set. + */ +static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) +{ +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + char *new_ins = NULL; + if(nfconvert_utf8) { + int new_ins_len; + new_ins_len = ntfs_macosx_normalize_utf8(ins, &new_ins, 1); // Normalize to composed form + if(new_ins_len >= 0) + ins = new_ins; + else + ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFC: %s\n", ins); + } +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + const char *t = ins; + u32 wc; + BOOL allocated; + ntfschar *outpos; + int shorts, ret = -1; + + shorts = utf8_to_utf16_size(ins); + if (shorts < 0) + goto fail; + + allocated = FALSE; + if (!*outs) { + *outs = ntfs_malloc((shorts + 1) * sizeof(ntfschar)); + if (!*outs) + goto fail; + allocated = TRUE; + } + + outpos = *outs; + + while(1) { + int m = utf8_to_unicode(&wc, t); + if (m <= 0) { + if (m < 0) { + /* do not leave space allocated if failed */ + if (allocated) { + free(*outs); + *outs = (ntfschar*)NULL; + } + goto fail; + } + *outpos++ = const_cpu_to_le16(0); + break; + } + if (wc < 0x10000) + *outpos++ = cpu_to_le16(wc); + else { + wc -= 0x10000; + *outpos++ = cpu_to_le16((wc >> 10) + 0xd800); + *outpos++ = cpu_to_le16((wc & 0x3ff) + 0xdc00); + } + t += m; + } + + ret = --outpos - *outs; +fail: +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + if(new_ins != NULL) + free(new_ins); +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + return ret; +} + +/** + * ntfs_ucstombs - convert a little endian Unicode string to a multibyte string + * @ins: input Unicode string buffer + * @ins_len: length of input string in Unicode characters + * @outs: on return contains the (allocated) output multibyte string + * @outs_len: length of output buffer in bytes + * + * Convert the input little endian, 2-byte Unicode string @ins, of length + * @ins_len into the multibyte string format dictated by the current locale. + * + * If *@outs is NULL, the function allocates the string and the caller is + * responsible for calling free(*@outs); when finished with it. + * + * On success the function returns the number of bytes written to the output + * string *@outs (>= 0), not counting the terminating NULL byte. If the output + * string buffer was allocated, *@outs is set to it. + * + * On error, -1 is returned, and errno is set to the error code. The following + * error codes can be expected: + * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). + * EILSEQ The input string cannot be represented as a multibyte + * sequence according to the current locale. + * ENAMETOOLONG Destination buffer is too small for input string. + * ENOMEM Not enough memory to allocate destination buffer. + */ +int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, + int outs_len) +{ + char *mbs; + int mbs_len; +#ifdef MB_CUR_MAX + wchar_t wc; + int i, o; + int cnt = 0; +#ifdef HAVE_MBSINIT + mbstate_t mbstate; +#endif +#endif /* MB_CUR_MAX */ + + if (!ins || !outs) { + errno = EINVAL; + return -1; + } + mbs = *outs; + mbs_len = outs_len; + if (mbs && !mbs_len) { + errno = ENAMETOOLONG; + return -1; + } + if (use_utf8) + return ntfs_utf16_to_utf8(ins, ins_len, outs, outs_len); +#ifdef MB_CUR_MAX + if (!mbs) { + mbs_len = (ins_len + 1) * MB_CUR_MAX; + mbs = ntfs_malloc(mbs_len); + if (!mbs) + return -1; + } +#ifdef HAVE_MBSINIT + memset(&mbstate, 0, sizeof(mbstate)); +#else + wctomb(NULL, 0); +#endif + for (i = o = 0; i < ins_len; i++) { + /* Reallocate memory if necessary or abort. */ + if ((int)(o + MB_CUR_MAX) > mbs_len) { + char *tc; + if (mbs == *outs) { + errno = ENAMETOOLONG; + return -1; + } + tc = ntfs_malloc((mbs_len + 64) & ~63); + if (!tc) + goto err_out; + memcpy(tc, mbs, mbs_len); + mbs_len = (mbs_len + 64) & ~63; + free(mbs); + mbs = tc; + } + /* Convert the LE Unicode character to a CPU wide character. */ + wc = (wchar_t)le16_to_cpu(ins[i]); + if (!wc) + break; + /* Convert the CPU endian wide character to multibyte. */ +#ifdef HAVE_MBSINIT + cnt = wcrtomb(mbs + o, wc, &mbstate); +#else + cnt = wctomb(mbs + o, wc); +#endif + if (cnt == -1) + goto err_out; + if (cnt <= 0) { + ntfs_log_debug("Eeek. cnt <= 0, cnt = %i\n", cnt); + errno = EINVAL; + goto err_out; + } + o += cnt; + } +#ifdef HAVE_MBSINIT + /* Make sure we are back in the initial state. */ + if (!mbsinit(&mbstate)) { + ntfs_log_debug("Eeek. mbstate not in initial state!\n"); + errno = EILSEQ; + goto err_out; + } +#endif + /* Now write the NULL character. */ + mbs[o] = '\0'; + if (*outs != mbs) + *outs = mbs; + return o; +err_out: + if (mbs != *outs) { + int eo = errno; + free(mbs); + errno = eo; + } +#else /* MB_CUR_MAX */ + errno = EILSEQ; +#endif /* MB_CUR_MAX */ + return -1; +} + +/** + * ntfs_mbstoucs - convert a multibyte string to a little endian Unicode string + * @ins: input multibyte string buffer + * @outs: on return contains the (allocated) output Unicode string + * + * Convert the input multibyte string @ins, from the current locale into the + * corresponding little endian, 2-byte Unicode string. + * + * The function allocates the string and the caller is responsible for calling + * free(*@outs); when finished with it. + * + * On success the function returns the number of Unicode characters written to + * the output string *@outs (>= 0), not counting the terminating Unicode NULL + * character. + * + * On error, -1 is returned, and errno is set to the error code. The following + * error codes can be expected: + * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). + * EILSEQ The input string cannot be represented as a Unicode + * string according to the current locale. + * ENAMETOOLONG Destination buffer is too small for input string. + * ENOMEM Not enough memory to allocate destination buffer. + */ +int ntfs_mbstoucs(const char *ins, ntfschar **outs) +{ +#ifdef MB_CUR_MAX + ntfschar *ucs; + const char *s; + wchar_t wc; + int i, o, cnt, ins_len, ucs_len, ins_size; +#ifdef HAVE_MBSINIT + mbstate_t mbstate; +#endif +#endif /* MB_CUR_MAX */ + + if (!ins || !outs) { + errno = EINVAL; + return -1; + } + + if (use_utf8) + return ntfs_utf8_to_utf16(ins, outs); + +#ifdef MB_CUR_MAX + /* Determine the size of the multi-byte string in bytes. */ + ins_size = strlen(ins); + /* Determine the length of the multi-byte string. */ + s = ins; +#if defined(HAVE_MBSINIT) + memset(&mbstate, 0, sizeof(mbstate)); + ins_len = mbsrtowcs(NULL, (const char **)&s, 0, &mbstate); +#ifdef __CYGWIN32__ + if (!ins_len && *ins) { + /* Older Cygwin had broken mbsrtowcs() implementation. */ + ins_len = strlen(ins); + } +#endif +#elif !defined(DJGPP) + ins_len = mbstowcs(NULL, s, 0); +#else + /* Eeek!!! DJGPP has broken mbstowcs() implementation!!! */ + ins_len = strlen(ins); +#endif + if (ins_len == -1) + return ins_len; +#ifdef HAVE_MBSINIT + if ((s != ins) || !mbsinit(&mbstate)) { +#else + if (s != ins) { +#endif + errno = EILSEQ; + return -1; + } + /* Add the NULL terminator. */ + ins_len++; + ucs_len = ins_len; + ucs = ntfs_malloc(ucs_len * sizeof(ntfschar)); + if (!ucs) + return -1; +#ifdef HAVE_MBSINIT + memset(&mbstate, 0, sizeof(mbstate)); +#else + mbtowc(NULL, NULL, 0); +#endif + for (i = o = cnt = 0; i < ins_size; i += cnt, o++) { + /* Reallocate memory if necessary. */ + if (o >= ucs_len) { + ntfschar *tc; + ucs_len = (ucs_len * sizeof(ntfschar) + 64) & ~63; + tc = realloc(ucs, ucs_len); + if (!tc) + goto err_out; + ucs = tc; + ucs_len /= sizeof(ntfschar); + } + /* Convert the multibyte character to a wide character. */ +#ifdef HAVE_MBSINIT + cnt = mbrtowc(&wc, ins + i, ins_size - i, &mbstate); +#else + cnt = mbtowc(&wc, ins + i, ins_size - i); +#endif + if (!cnt) + break; + if (cnt == -1) + goto err_out; + if (cnt < -1) { + ntfs_log_trace("Eeek. cnt = %i\n", cnt); + errno = EINVAL; + goto err_out; + } + /* Make sure we are not overflowing the NTFS Unicode set. */ + if ((unsigned long)wc >= (unsigned long)(1 << + (8 * sizeof(ntfschar)))) { + errno = EILSEQ; + goto err_out; + } + /* Convert the CPU wide character to a LE Unicode character. */ + ucs[o] = cpu_to_le16(wc); + } +#ifdef HAVE_MBSINIT + /* Make sure we are back in the initial state. */ + if (!mbsinit(&mbstate)) { + ntfs_log_trace("Eeek. mbstate not in initial state!\n"); + errno = EILSEQ; + goto err_out; + } +#endif + /* Now write the NULL character. */ + ucs[o] = cpu_to_le16(L'\0'); + *outs = ucs; + return o; +err_out: + free(ucs); +#else /* MB_CUR_MAX */ + errno = EILSEQ; +#endif /* MB_CUR_MAX */ + return -1; +} + +/* + * Turn a UTF8 name uppercase + * + * Returns an allocated uppercase name which has to be freed by caller + * or NULL if there is an error (described by errno) + */ + +char *ntfs_uppercase_mbs(const char *low, + const ntfschar *upcase, u32 upcase_size) +{ + int size; + char *upp; + u32 wc; + int n; + const char *s; + char *t; + + size = strlen(low); + upp = (char*)ntfs_malloc(3*size + 1); + if (upp) { + s = low; + t = upp; + do { + n = utf8_to_unicode(&wc, s); + if (n > 0) { + if (wc < upcase_size) + wc = le16_to_cpu(upcase[wc]); + if (wc < 0x80) + *t++ = wc; + else if (wc < 0x800) { + *t++ = (0xc0 | ((wc >> 6) & 0x3f)); + *t++ = 0x80 | (wc & 0x3f); + } else if (wc < 0x10000) { + *t++ = 0xe0 | (wc >> 12); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } else { + *t++ = 0xf0 | ((wc >> 18) & 7); + *t++ = 0x80 | ((wc >> 12) & 63); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } + s += n; + } + } while (n > 0); + if (n < 0) { + free(upp); + upp = (char*)NULL; + errno = EILSEQ; + } + *t = 0; + } + return (upp); +} + +/** + * ntfs_upcase_table_build - build the default upcase table for NTFS + * @uc: destination buffer where to store the built table + * @uc_len: size of destination buffer in bytes + * + * ntfs_upcase_table_build() builds the default upcase table for NTFS and + * stores it in the caller supplied buffer @uc of size @uc_len. + * + * Note, @uc_len must be at least 128kiB in size or bad things will happen! + */ +void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) +{ +#if 1 /* Vista */ + /* + * This is the table as defined by Vista + */ + /* + * "Start" is inclusive and "End" is exclusive, every value has the + * value of "Add" added to it. + */ + static int uc_run_table[][3] = { /* Start, End, Add */ + {0x0061, 0x007b, -32}, {0x00e0, 0x00f7, -32}, {0x00f8, 0x00ff, -32}, + {0x0256, 0x0258, -205}, {0x028a, 0x028c, -217}, {0x037b, 0x037e, 130}, + {0x03ac, 0x03ad, -38}, {0x03ad, 0x03b0, -37}, {0x03b1, 0x03c2, -32}, + {0x03c2, 0x03c3, -31}, {0x03c3, 0x03cc, -32}, {0x03cc, 0x03cd, -64}, + {0x03cd, 0x03cf, -63}, {0x0430, 0x0450, -32}, {0x0450, 0x0460, -80}, + {0x0561, 0x0587, -48}, {0x1f00, 0x1f08, 8}, {0x1f10, 0x1f16, 8}, + {0x1f20, 0x1f28, 8}, {0x1f30, 0x1f38, 8}, {0x1f40, 0x1f46, 8}, + {0x1f51, 0x1f52, 8}, {0x1f53, 0x1f54, 8}, {0x1f55, 0x1f56, 8}, + {0x1f57, 0x1f58, 8}, {0x1f60, 0x1f68, 8}, {0x1f70, 0x1f72, 74}, + {0x1f72, 0x1f76, 86}, {0x1f76, 0x1f78, 100}, {0x1f78, 0x1f7a, 128}, + {0x1f7a, 0x1f7c, 112}, {0x1f7c, 0x1f7e, 126}, {0x1f80, 0x1f88, 8}, + {0x1f90, 0x1f98, 8}, {0x1fa0, 0x1fa8, 8}, {0x1fb0, 0x1fb2, 8}, + {0x1fb3, 0x1fb4, 9}, {0x1fcc, 0x1fcd, -9}, {0x1fd0, 0x1fd2, 8}, + {0x1fe0, 0x1fe2, 8}, {0x1fe5, 0x1fe6, 7}, {0x1ffc, 0x1ffd, -9}, + {0x2170, 0x2180, -16}, {0x24d0, 0x24ea, -26}, {0x2c30, 0x2c5f, -48}, + {0x2d00, 0x2d26, -7264}, {0xff41, 0xff5b, -32}, {0} + }; + /* + * "Start" is exclusive and "End" is inclusive, every second value is + * decremented by one. + */ + static int uc_dup_table[][2] = { /* Start, End */ + {0x0100, 0x012f}, {0x0132, 0x0137}, {0x0139, 0x0149}, {0x014a, 0x0178}, + {0x0179, 0x017e}, {0x01a0, 0x01a6}, {0x01b3, 0x01b7}, {0x01cd, 0x01dd}, + {0x01de, 0x01ef}, {0x01f4, 0x01f5}, {0x01f8, 0x01f9}, {0x01fa, 0x0220}, + {0x0222, 0x0234}, {0x023b, 0x023c}, {0x0241, 0x0242}, {0x0246, 0x024f}, + {0x03d8, 0x03ef}, {0x03f7, 0x03f8}, {0x03fa, 0x03fb}, {0x0460, 0x0481}, + {0x048a, 0x04bf}, {0x04c1, 0x04c4}, {0x04c5, 0x04c8}, {0x04c9, 0x04ce}, + {0x04ec, 0x04ed}, {0x04d0, 0x04eb}, {0x04ee, 0x04f5}, {0x04f6, 0x0513}, + {0x1e00, 0x1e95}, {0x1ea0, 0x1ef9}, {0x2183, 0x2184}, {0x2c60, 0x2c61}, + {0x2c67, 0x2c6c}, {0x2c75, 0x2c76}, {0x2c80, 0x2ce3}, {0} + }; + /* + * Set the Unicode character at offset "Offset" to "Value". Note, + * "Value" is host endian. + */ + static int uc_byte_table[][2] = { /* Offset, Value */ + {0x00ff, 0x0178}, {0x0180, 0x0243}, {0x0183, 0x0182}, {0x0185, 0x0184}, + {0x0188, 0x0187}, {0x018c, 0x018b}, {0x0192, 0x0191}, {0x0195, 0x01f6}, + {0x0199, 0x0198}, {0x019a, 0x023d}, {0x019e, 0x0220}, {0x01a8, 0x01a7}, + {0x01ad, 0x01ac}, {0x01b0, 0x01af}, {0x01b9, 0x01b8}, {0x01bd, 0x01bc}, + {0x01bf, 0x01f7}, {0x01c6, 0x01c4}, {0x01c9, 0x01c7}, {0x01cc, 0x01ca}, + {0x01dd, 0x018e}, {0x01f3, 0x01f1}, {0x023a, 0x2c65}, {0x023e, 0x2c66}, + {0x0253, 0x0181}, {0x0254, 0x0186}, {0x0259, 0x018f}, {0x025b, 0x0190}, + {0x0260, 0x0193}, {0x0263, 0x0194}, {0x0268, 0x0197}, {0x0269, 0x0196}, + {0x026b, 0x2c62}, {0x026f, 0x019c}, {0x0272, 0x019d}, {0x0275, 0x019f}, + {0x027d, 0x2c64}, {0x0280, 0x01a6}, {0x0283, 0x01a9}, {0x0288, 0x01ae}, + {0x0289, 0x0244}, {0x028c, 0x0245}, {0x0292, 0x01b7}, {0x03f2, 0x03f9}, + {0x04cf, 0x04c0}, {0x1d7d, 0x2c63}, {0x214e, 0x2132}, {0} + }; +#else /* Vista */ + /* + * This is the table as defined by Windows XP + */ + static int uc_run_table[][3] = { /* Start, End, Add */ + {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, + {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, + {0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100}, + {0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128}, + {0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112}, + {0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126}, + {0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8}, + {0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8}, + {0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8}, + {0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7}, + {0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16}, + {0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26}, + {0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32}, + {0} + }; + static int uc_dup_table[][2] = { /* Start, End */ + {0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC}, + {0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB}, + {0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5}, + {0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9}, + {0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95}, + {0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9}, + {0} + }; + static int uc_byte_table[][2] = { /* Offset, Value */ + {0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196}, + {0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C}, + {0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D}, + {0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F}, + {0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9}, + {0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE}, + {0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7}, + {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, + {0} + }; +#endif /* Vista */ + int i, r; + int k, off; + + memset((char*)uc, 0, uc_len); + uc_len >>= 1; + if (uc_len > 65536) + uc_len = 65536; + for (i = 0; (u32)i < uc_len; i++) + uc[i] = cpu_to_le16(i); + for (r = 0; uc_run_table[r][0]; r++) { + off = uc_run_table[r][2]; + for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++) + uc[i] = cpu_to_le16(i + off); + } + for (r = 0; uc_dup_table[r][0]; r++) + for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2) + uc[i + 1] = cpu_to_le16(i); + for (r = 0; uc_byte_table[r][0]; r++) { + k = uc_byte_table[r][1]; + uc[uc_byte_table[r][0]] = cpu_to_le16(k); + } +} + +/* + * Allocate and build the default upcase table + * + * Returns the number of entries + * 0 if failed + */ + +#define UPCASE_LEN 65536 /* default number of entries in upcase */ + +u32 ntfs_upcase_build_default(ntfschar **upcase) +{ + u32 upcase_len = 0; + + *upcase = (ntfschar*)ntfs_malloc(UPCASE_LEN*2); + if (*upcase) { + ntfs_upcase_table_build(*upcase, UPCASE_LEN*2); + upcase_len = UPCASE_LEN; + } + return (upcase_len); +} + +/* + * Build a table for converting to lower case + * + * This is only meaningful when there is a single lower case + * character leading to an upper case one, and currently the + * only exception is the greek letter sigma which has a single + * upper case glyph (code U+03A3), but two lower case glyphs + * (code U+03C3 and U+03C2, the latter to be used at the end + * of a word). In the following implementation the upper case + * sigma will be lowercased as U+03C3. + */ + +ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt) +{ + ntfschar *lc; + u32 upp; + u32 i; + + lc = (ntfschar*)ntfs_malloc(uc_cnt*sizeof(ntfschar)); + if (lc) { + for (i=0; i<uc_cnt; i++) + lc[i] = cpu_to_le16(i); + for (i=0; i<uc_cnt; i++) { + upp = le16_to_cpu(uc[i]); + if ((upp != i) && (upp < uc_cnt)) + lc[upp] = cpu_to_le16(i); + } + } else + ntfs_log_error("Could not build the locase table\n"); + return (lc); +} + +/** + * ntfs_str2ucs - convert a string to a valid NTFS file name + * @s: input string + * @len: length of output buffer in Unicode characters + * + * Convert the input @s string into the corresponding little endian, + * 2-byte Unicode string. The length of the converted string is less + * or equal to the maximum length allowed by the NTFS format (255). + * + * If @s is NULL then return AT_UNNAMED. + * + * On success the function returns the Unicode string in an allocated + * buffer and the caller is responsible to free it when it's not needed + * anymore. + * + * On error NULL is returned and errno is set to the error code. + */ +ntfschar *ntfs_str2ucs(const char *s, int *len) +{ + ntfschar *ucs = NULL; + + if (s && ((*len = ntfs_mbstoucs(s, &ucs)) == -1)) { + ntfs_log_perror("Couldn't convert '%s' to Unicode", s); + return NULL; + } + if (*len > NTFS_MAX_NAME_LEN) { + free(ucs); + errno = ENAMETOOLONG; + return NULL; + } + if (!ucs || !*len) { + ucs = AT_UNNAMED; + *len = 0; + } + return ucs; +} + +/** + * ntfs_ucsfree - free memory allocated by ntfs_str2ucs() + * @ucs input string to be freed + * + * Free memory at @ucs and which was allocated by ntfs_str2ucs. + * + * Return value: none. + */ +void ntfs_ucsfree(ntfschar *ucs) +{ + if (ucs && (ucs != AT_UNNAMED)) + free(ucs); +} + +/* + * Check whether a name contains no chars forbidden + * for DOS or Win32 use + * + * If there is a bad char, errno is set to EINVAL + */ + +BOOL ntfs_forbidden_chars(const ntfschar *name, int len) +{ + BOOL forbidden; + int ch; + int i; + static const u32 mainset = (1L << ('\"' - 0x20)) + | (1L << ('*' - 0x20)) + | (1L << ('/' - 0x20)) + | (1L << (':' - 0x20)) + | (1L << ('<' - 0x20)) + | (1L << ('>' - 0x20)) + | (1L << ('?' - 0x20)); + + forbidden = (len == 0) + || (name[len-1] == const_cpu_to_le16(' ')) + || (name[len-1] == const_cpu_to_le16('.')); + for (i=0; i<len; i++) { + ch = le16_to_cpu(name[i]); + if ((ch < 0x20) + || ((ch < 0x40) + && ((1L << (ch - 0x20)) & mainset)) + || (ch == '\\') + || (ch == '|')) + forbidden = TRUE; + } + if (forbidden) + errno = EINVAL; + return (forbidden); +} + +/* + * Check whether a name contains no forbidden chars and + * is not a reserved name for DOS or Win32 use + * + * The reserved names are CON, PRN, AUX, NUL, COM1..COM9, LPT1..LPT9 + * with no suffix or any suffix. + * + * If the name is forbidden, errno is set to EINVAL + */ + +BOOL ntfs_forbidden_names(ntfs_volume *vol, const ntfschar *name, int len) +{ + BOOL forbidden; + int h; + static const ntfschar dot = const_cpu_to_le16('.'); + static const ntfschar con[] = { const_cpu_to_le16('c'), + const_cpu_to_le16('o'), const_cpu_to_le16('n') }; + static const ntfschar prn[] = { const_cpu_to_le16('p'), + const_cpu_to_le16('r'), const_cpu_to_le16('n') }; + static const ntfschar aux[] = { const_cpu_to_le16('a'), + const_cpu_to_le16('u'), const_cpu_to_le16('x') }; + static const ntfschar nul[] = { const_cpu_to_le16('n'), + const_cpu_to_le16('u'), const_cpu_to_le16('l') }; + static const ntfschar com[] = { const_cpu_to_le16('c'), + const_cpu_to_le16('o'), const_cpu_to_le16('m') }; + static const ntfschar lpt[] = { const_cpu_to_le16('l'), + const_cpu_to_le16('p'), const_cpu_to_le16('t') }; + + forbidden = ntfs_forbidden_chars(name, len); + if (!forbidden && (len >= 3)) { + /* + * Rough hash check to tell whether the first couple of chars + * may be one of CO PR AU NU LP or lowercase variants. + */ + h = ((le16_to_cpu(name[0]) & 31)*48) + ^ ((le16_to_cpu(name[1]) & 31)*165); + if ((h % 23) == 17) { + /* do a full check, depending on the third char */ + switch (le16_to_cpu(name[2]) & ~0x20) { + case 'N' : + if (((len == 3) || (name[3] == dot)) + && (!ntfs_ucsncasecmp(name, con, 3, + vol->upcase, vol->upcase_len) + || !ntfs_ucsncasecmp(name, prn, 3, + vol->upcase, vol->upcase_len))) + forbidden = TRUE; + break; + case 'X' : + if (((len == 3) || (name[3] == dot)) + && !ntfs_ucsncasecmp(name, aux, 3, + vol->upcase, vol->upcase_len)) + forbidden = TRUE; + break; + case 'L' : + if (((len == 3) || (name[3] == dot)) + && !ntfs_ucsncasecmp(name, nul, 3, + vol->upcase, vol->upcase_len)) + forbidden = TRUE; + break; + case 'M' : + if ((len > 3) + && (le16_to_cpu(name[3]) >= '1') + && (le16_to_cpu(name[3]) <= '9') + && ((len == 4) || (name[4] == dot)) + && !ntfs_ucsncasecmp(name, com, 3, + vol->upcase, vol->upcase_len)) + forbidden = TRUE; + break; + case 'T' : + if ((len > 3) + && (le16_to_cpu(name[3]) >= '1') + && (le16_to_cpu(name[3]) <= '9') + && ((len == 4) || (name[4] == dot)) + && !ntfs_ucsncasecmp(name, lpt, 3, + vol->upcase, vol->upcase_len)) + forbidden = TRUE; + break; + } + } + } + + if (forbidden) + errno = EINVAL; + return (forbidden); +} + +/* + * Check whether the same name can be used as a DOS and + * a Win32 name + * + * The names must be the same, or the short name the uppercase + * variant of the long name + */ + +BOOL ntfs_collapsible_chars(ntfs_volume *vol, + const ntfschar *shortname, int shortlen, + const ntfschar *longname, int longlen) +{ + BOOL collapsible; + unsigned int ch; + unsigned int cs; + int i; + + collapsible = shortlen == longlen; + for (i=0; collapsible && (i<shortlen); i++) { + ch = le16_to_cpu(longname[i]); + cs = le16_to_cpu(shortname[i]); + if ((cs != ch) + && ((ch >= vol->upcase_len) + || (cs >= vol->upcase_len) + || (vol->upcase[cs] != vol->upcase[ch]))) + collapsible = FALSE; + } + return (collapsible); +} + +/* + * Define the character encoding to be used. + * Use UTF-8 unless specified otherwise. + */ + +int ntfs_set_char_encoding(const char *locale) +{ + use_utf8 = 0; + if (!locale || strstr(locale,"utf8") || strstr(locale,"UTF8") + || strstr(locale,"utf-8") || strstr(locale,"UTF-8")) + use_utf8 = 1; + else + if (setlocale(LC_ALL, locale)) + use_utf8 = 0; + else { + ntfs_log_error("Invalid locale, encoding to UTF-8\n"); + use_utf8 = 1; + } + return 0; /* always successful */ +} + +#if defined(__APPLE__) || defined(__DARWIN__) + +int ntfs_macosx_normalize_filenames(int normalize) { +#ifdef ENABLE_NFCONV + if(normalize == 0 || normalize == 1) { + nfconvert_utf8 = normalize; + return 0; + } + else + return -1; +#else + return -1; +#endif /* ENABLE_NFCONV */ +} + +int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, + int composed) { +#ifdef ENABLE_NFCONV + /* For this code to compile, the CoreFoundation framework must be fed to the linker. */ + CFStringRef cfSourceString; + CFMutableStringRef cfMutableString; + CFRange rangeToProcess; + CFIndex requiredBufferLength; + char *result = NULL; + int resultLength = -1; + + /* Convert the UTF-8 string to a CFString. */ + cfSourceString = CFStringCreateWithCString(kCFAllocatorDefault, utf8_string, kCFStringEncodingUTF8); + if(cfSourceString == NULL) { + ntfs_log_error("CFStringCreateWithCString failed!\n"); + return -2; + } + + /* Create a mutable string from cfSourceString that we are free to modify. */ + cfMutableString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfSourceString); + CFRelease(cfSourceString); /* End-of-life. */ + if(cfMutableString == NULL) { + ntfs_log_error("CFStringCreateMutableCopy failed!\n"); + return -3; + } + + /* Normalize the mutable string to the desired normalization form. */ + CFStringNormalize(cfMutableString, (composed != 0 ? kCFStringNormalizationFormC : kCFStringNormalizationFormD)); + + /* Store the resulting string in a '\0'-terminated UTF-8 encoded char* buffer. */ + rangeToProcess = CFRangeMake(0, CFStringGetLength(cfMutableString)); + if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8, 0, false, NULL, 0, &requiredBufferLength) > 0) { + resultLength = sizeof(char)*(requiredBufferLength + 1); + result = ntfs_calloc(resultLength); + + if(result != NULL) { + if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8, + 0, false, (UInt8*)result, resultLength-1, &requiredBufferLength) <= 0) { + ntfs_log_error("Could not perform UTF-8 conversion of normalized CFMutableString.\n"); + free(result); + result = NULL; + } + } + else + ntfs_log_error("Could not perform a ntfs_calloc of %d bytes for char *result.\n", resultLength); + } + else + ntfs_log_error("Could not perform check for required length of UTF-8 conversion of normalized CFMutableString.\n"); + + + CFRelease(cfMutableString); + + if(result != NULL) { + *target = result; + return resultLength - 1; + } + else + return -1; +#else + return -1; +#endif /* ENABLE_NFCONV */ +} +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ diff --git a/libntfs-3g/unix_io.c b/libntfs-3g/unix_io.c new file mode 100755 index 0000000000000000000000000000000000000000..17c2899deba3710d84d97f1dadc4999eee2deb41 --- /dev/null +++ b/libntfs-3g/unix_io.c @@ -0,0 +1,371 @@ +/** + * unix_io.c - Unix style disk io functions. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_LINUX_FD_H +#include <linux/fd.h> +#endif + +#include "types.h" +#include "mst.h" +#include "debug.h" +#include "device.h" +#include "logging.h" +#include "misc.h" + +#define DEV_FD(dev) (*(int *)dev->d_private) + +/* Define to nothing if not present on this system. */ +#ifndef O_EXCL +# define O_EXCL 0 +#endif + +/** + * fsync replacement which makes every effort to try to get the data down to + * disk, using different means for different operating systems. Specifically, + * it issues the proper fcntl for Mac OS X or does fsync where it is available + * or as a last resort calls the fsync function. Information on this problem + * was retrieved from: + * http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf + */ +static int ntfs_fsync(int fildes) +{ + int ret = -1; +#if defined(__APPLE__) || defined(__DARWIN__) +# ifndef F_FULLFSYNC +# error "Mac OS X: F_FULLFSYNC is not defined. Either you didn't include fcntl.h or you're using an older, unsupported version of Mac OS X (pre-10.3)." +# endif + /* + * Apple has disabled fsync() for internal disk drives in OS X. + * To force a synchronization of disk contents, we use a Mac OS X + * specific fcntl, F_FULLFSYNC. + */ + ret = fcntl(fildes, F_FULLFSYNC, NULL); + if (ret) { + /* + * If we are not on a file system that supports this, + * then fall back to a plain fsync. + */ + ret = fsync(fildes); + } +#else + ret = fsync(fildes); +#endif + return ret; +} + +/** + * ntfs_device_unix_io_open - Open a device and lock it exclusively + * @dev: + * @flags: + * + * Description... + * + * Returns: + */ +static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags) +{ + struct flock flk; + struct stat sbuf; + int err; + + if (NDevOpen(dev)) { + errno = EBUSY; + return -1; + } + if (stat(dev->d_name, &sbuf)) { + ntfs_log_perror("Failed to access '%s'", dev->d_name); + return -1; + } + if (S_ISBLK(sbuf.st_mode)) + NDevSetBlock(dev); + + dev->d_private = ntfs_malloc(sizeof(int)); + if (!dev->d_private) + return -1; + /* + * Open file for exclusive access if mounting r/w. + * Fuseblk takes care about block devices. + */ + if (!NDevBlock(dev) && (flags & O_RDWR) == O_RDWR) + flags |= O_EXCL; + *(int*)dev->d_private = open(dev->d_name, flags); + if (*(int*)dev->d_private == -1) { + err = errno; + goto err_out; + } + + if ((flags & O_RDWR) != O_RDWR) + NDevSetReadOnly(dev); + + memset(&flk, 0, sizeof(flk)); + if (NDevReadOnly(dev)) + flk.l_type = F_RDLCK; + else + flk.l_type = F_WRLCK; + flk.l_whence = SEEK_SET; + flk.l_start = flk.l_len = 0LL; + if (fcntl(DEV_FD(dev), F_SETLK, &flk)) { + err = errno; + ntfs_log_perror("Failed to %s lock '%s'", NDevReadOnly(dev) ? + "read" : "write", dev->d_name); + if (close(DEV_FD(dev))) + ntfs_log_perror("Failed to close '%s'", dev->d_name); + goto err_out; + } + + NDevSetOpen(dev); + return 0; +err_out: + free(dev->d_private); + dev->d_private = NULL; + errno = err; + return -1; +} + +/** + * ntfs_device_unix_io_close - Close the device, releasing the lock + * @dev: + * + * Description... + * + * Returns: + */ +static int ntfs_device_unix_io_close(struct ntfs_device *dev) +{ + struct flock flk; + + if (!NDevOpen(dev)) { + errno = EBADF; + ntfs_log_perror("Device %s is not open", dev->d_name); + return -1; + } + if (NDevDirty(dev)) + if (ntfs_fsync(DEV_FD(dev))) { + ntfs_log_perror("Failed to fsync device %s", dev->d_name); + return -1; + } + + memset(&flk, 0, sizeof(flk)); + flk.l_type = F_UNLCK; + flk.l_whence = SEEK_SET; + flk.l_start = flk.l_len = 0LL; + if (fcntl(DEV_FD(dev), F_SETLK, &flk)) + ntfs_log_perror("Could not unlock %s", dev->d_name); + if (close(DEV_FD(dev))) { + ntfs_log_perror("Failed to close device %s", dev->d_name); + return -1; + } + NDevClearOpen(dev); + free(dev->d_private); + dev->d_private = NULL; + return 0; +} + +/** + * ntfs_device_unix_io_seek - Seek to a place on the device + * @dev: + * @offset: + * @whence: + * + * Description... + * + * Returns: + */ +static s64 ntfs_device_unix_io_seek(struct ntfs_device *dev, s64 offset, + int whence) +{ + return lseek64(DEV_FD(dev), offset, whence); +} + +/** + * ntfs_device_unix_io_read - Read from the device, from the current location + * @dev: + * @buf: + * @count: + * + * Description... + * + * Returns: + */ +static s64 ntfs_device_unix_io_read(struct ntfs_device *dev, void *buf, + s64 count) +{ + return read(DEV_FD(dev), buf, count); +} + +/** + * ntfs_device_unix_io_write - Write to the device, at the current location + * @dev: + * @buf: + * @count: + * + * Description... + * + * Returns: + */ +static s64 ntfs_device_unix_io_write(struct ntfs_device *dev, const void *buf, + s64 count) +{ + if (NDevReadOnly(dev)) { + errno = EROFS; + return -1; + } + NDevSetDirty(dev); + return write(DEV_FD(dev), buf, count); +} + +/** + * ntfs_device_unix_io_pread - Perform a positioned read from the device + * @dev: + * @buf: + * @count: + * @offset: + * + * Description... + * + * Returns: + */ +static s64 ntfs_device_unix_io_pread(struct ntfs_device *dev, void *buf, + s64 count, s64 offset) +{ + return pread64(DEV_FD(dev), buf, count, offset); +} + +/** + * ntfs_device_unix_io_pwrite - Perform a positioned write to the device + * @dev: + * @buf: + * @count: + * @offset: + * + * Description... + * + * Returns: + */ +static s64 ntfs_device_unix_io_pwrite(struct ntfs_device *dev, const void *buf, + s64 count, s64 offset) +{ + if (NDevReadOnly(dev)) { + errno = EROFS; + return -1; + } + NDevSetDirty(dev); + return pwrite64(DEV_FD(dev), buf, count, offset); +} + +/** + * ntfs_device_unix_io_sync - Flush any buffered changes to the device + * @dev: + * + * Description... + * + * Returns: + */ +static int ntfs_device_unix_io_sync(struct ntfs_device *dev) +{ + int res = 0; + + if (!NDevReadOnly(dev)) { + res = ntfs_fsync(DEV_FD(dev)); + if (res) + ntfs_log_perror("Failed to sync device %s", dev->d_name); + else + NDevClearDirty(dev); + } + return res; +} + +/** + * ntfs_device_unix_io_stat - Get information about the device + * @dev: + * @buf: + * + * Description... + * + * Returns: + */ +static int ntfs_device_unix_io_stat(struct ntfs_device *dev, struct stat *buf) +{ + return fstat(DEV_FD(dev), buf); +} + +/** + * ntfs_device_unix_io_ioctl - Perform an ioctl on the device + * @dev: + * @request: + * @argp: + * + * Description... + * + * Returns: + */ +static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev, int request, + void *argp) +{ + return ioctl(DEV_FD(dev), request, argp); +} + +/** + * Device operations for working with unix style devices and files. + */ +struct ntfs_device_operations ntfs_device_unix_io_ops = { + .open = ntfs_device_unix_io_open, + .close = ntfs_device_unix_io_close, + .seek = ntfs_device_unix_io_seek, + .read = ntfs_device_unix_io_read, + .write = ntfs_device_unix_io_write, + .pread = ntfs_device_unix_io_pread, + .pwrite = ntfs_device_unix_io_pwrite, + .sync = ntfs_device_unix_io_sync, + .stat = ntfs_device_unix_io_stat, + .ioctl = ntfs_device_unix_io_ioctl, +}; diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c new file mode 100755 index 0000000000000000000000000000000000000000..edd769796e8743d8b81a5a449cb34a913a9db4a7 --- /dev/null +++ b/libntfs-3g/volume.c @@ -0,0 +1,1936 @@ +/** + * volume.c - NTFS volume handling code. Originated from the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * Copyright (c) 2002-2009 Szabolcs Szakacsits + * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2010 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif + +#if defined(__sun) && defined (__SVR4) +#include <sys/mnttab.h> +#endif + +#include "param.h" +#include "compat.h" +#include "volume.h" +#include "attrib.h" +#include "mft.h" +#include "bootsect.h" +#include "device.h" +#include "debug.h" +#include "inode.h" +#include "runlist.h" +#include "logfile.h" +#include "dir.h" +#include "logging.h" +#include "cache.h" +#include "realpath.h" +#include "misc.h" + +const char *ntfs_home = +"News, support and information: http://tuxera.com\n"; + +static const char *invalid_ntfs_msg = +"The device '%s' doesn't seem to have a valid NTFS.\n" +"Maybe the wrong device is used? Or the whole disk instead of a\n" +"partition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?\n"; + +static const char *corrupt_volume_msg = +"NTFS is either inconsistent, or there is a hardware fault, or it's a\n" +"SoftRAID/FakeRAID hardware. In the first case run chkdsk /f on Windows\n" +"then reboot into Windows twice. The usage of the /f parameter is very\n" +"important! If the device is a SoftRAID/FakeRAID then first activate\n" +"it and mount a different device under the /dev/mapper/ directory, (e.g.\n" +"/dev/mapper/nvidia_eahaabcc1). Please see the 'dmraid' documentation\n" +"for more details.\n"; + +static const char *hibernated_volume_msg = +"The NTFS partition is in an unsafe state. Please resume and shutdown\n" +"Windows fully (no hibernation or fast restarting), or mount the volume\n" +"read-only with the 'ro' mount option.\n"; + +static const char *unclean_journal_msg = +"Write access is denied because the disk wasn't safely powered\n" +"off and the 'norecover' mount option was specified.\n"; + +static const char *opened_volume_msg = +"Mount is denied because the NTFS volume is already exclusively opened.\n" +"The volume may be already mounted, or another software may use it which\n" +"could be identified for example by the help of the 'fuser' command.\n"; + +static const char *fakeraid_msg = +"Either the device is missing or it's powered down, or you have\n" +"SoftRAID hardware and must use an activated, different device under\n" +"/dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1) to mount NTFS.\n" +"Please see the 'dmraid' documentation for help.\n"; + +static const char *access_denied_msg = +"Please check '%s' and the ntfs-3g binary permissions,\n" +"and the mounting user ID. More explanation is provided at\n" +"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; + +/** + * ntfs_volume_alloc - Create an NTFS volume object and initialise it + * + * Description... + * + * Returns: + */ +ntfs_volume *ntfs_volume_alloc(void) +{ + return ntfs_calloc(sizeof(ntfs_volume)); +} + +static void ntfs_attr_free(ntfs_attr **na) +{ + if (na && *na) { + ntfs_attr_close(*na); + *na = NULL; + } +} + +static int ntfs_inode_free(ntfs_inode **ni) +{ + int ret = -1; + + if (ni && *ni) { + ret = ntfs_inode_close(*ni); + *ni = NULL; + } + + return ret; +} + +static void ntfs_error_set(int *err) +{ + if (!*err) + *err = errno; +} + +/** + * __ntfs_volume_release - Destroy an NTFS volume object + * @v: + * + * Description... + * + * Returns: + */ +static int __ntfs_volume_release(ntfs_volume *v) +{ + int err = 0; + + if (ntfs_inode_free(&v->vol_ni)) + ntfs_error_set(&err); + /* + * FIXME: Inodes must be synced before closing + * attributes, otherwise unmount could fail. + */ + if (v->lcnbmp_ni && NInoDirty(v->lcnbmp_ni)) + ntfs_inode_sync(v->lcnbmp_ni); + ntfs_attr_free(&v->lcnbmp_na); + if (ntfs_inode_free(&v->lcnbmp_ni)) + ntfs_error_set(&err); + + if (v->mft_ni && NInoDirty(v->mft_ni)) + ntfs_inode_sync(v->mft_ni); + ntfs_attr_free(&v->mftbmp_na); + ntfs_attr_free(&v->mft_na); + if (ntfs_inode_free(&v->mft_ni)) + ntfs_error_set(&err); + + if (v->mftmirr_ni && NInoDirty(v->mftmirr_ni)) + ntfs_inode_sync(v->mftmirr_ni); + ntfs_attr_free(&v->mftmirr_na); + if (ntfs_inode_free(&v->mftmirr_ni)) + ntfs_error_set(&err); + + if (v->dev) { + struct ntfs_device *dev = v->dev; + + if (dev->d_ops->sync(dev)) + ntfs_error_set(&err); + if (dev->d_ops->close(dev)) + ntfs_error_set(&err); + } + + ntfs_free_lru_caches(v); + free(v->vol_name); + free(v->upcase); + if (v->locase) free(v->locase); + free(v->attrdef); + free(v); + + errno = err; + return errno ? -1 : 0; +} + +static void ntfs_attr_setup_flag(ntfs_inode *ni) +{ + STANDARD_INFORMATION *si; + + si = ntfs_attr_readall(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, NULL); + if (si) { + ni->flags = si->file_attributes; + free(si); + } +} + +/** + * ntfs_mft_load - load the $MFT and setup the ntfs volume with it + * @vol: ntfs volume whose $MFT to load + * + * Load $MFT from @vol and setup @vol with it. After calling this function the + * volume @vol is ready for use by all read access functions provided by the + * ntfs library. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mft_load(ntfs_volume *vol) +{ + VCN next_vcn, last_vcn, highest_vcn; + s64 l; + MFT_RECORD *mb = NULL; + ntfs_attr_search_ctx *ctx = NULL; + ATTR_RECORD *a; + int eo; + + /* Manually setup an ntfs_inode. */ + vol->mft_ni = ntfs_inode_allocate(vol); + mb = ntfs_malloc(vol->mft_record_size); + if (!vol->mft_ni || !mb) { + ntfs_log_perror("Error allocating memory for $MFT"); + goto error_exit; + } + vol->mft_ni->mft_no = 0; + vol->mft_ni->mrec = mb; + /* Can't use any of the higher level functions yet! */ + l = ntfs_mst_pread(vol->dev, vol->mft_lcn << vol->cluster_size_bits, 1, + vol->mft_record_size, mb); + if (l != 1) { + if (l != -1) + errno = EIO; + ntfs_log_perror("Error reading $MFT"); + goto error_exit; + } + + if (ntfs_mft_record_check(vol, 0, mb)) + goto error_exit; + + ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL); + if (!ctx) + goto error_exit; + + /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */ + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + if (errno != ENOENT) { + ntfs_log_error("$MFT has corrupt attribute list.\n"); + goto io_error_exit; + } + goto mft_has_no_attr_list; + } + NInoSetAttrList(vol->mft_ni); + l = ntfs_get_attribute_value_length(ctx->attr); + if (l <= 0 || l > 0x40000) { + ntfs_log_error("$MFT/$ATTR_LIST invalid length (%lld).\n", + (long long)l); + goto io_error_exit; + } + vol->mft_ni->attr_list_size = l; + vol->mft_ni->attr_list = ntfs_malloc(l); + if (!vol->mft_ni->attr_list) + goto error_exit; + + l = ntfs_get_attribute_value(vol, ctx->attr, vol->mft_ni->attr_list); + if (!l) { + ntfs_log_error("Failed to get value of $MFT/$ATTR_LIST.\n"); + goto io_error_exit; + } + if (l != vol->mft_ni->attr_list_size) { + ntfs_log_error("Partial read of $MFT/$ATTR_LIST (%lld != " + "%u).\n", (long long)l, + vol->mft_ni->attr_list_size); + goto io_error_exit; + } + +mft_has_no_attr_list: + + ntfs_attr_setup_flag(vol->mft_ni); + + /* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */ + + /* Get an ntfs attribute for $MFT/$DATA and set it up, too. */ + vol->mft_na = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->mft_na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* Read all extents from the $DATA attribute in $MFT. */ + ntfs_attr_reinit_search_ctx(ctx); + last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits; + highest_vcn = next_vcn = 0; + a = NULL; + while (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, next_vcn, NULL, 0, + ctx)) { + runlist_element *nrl; + + a = ctx->attr; + /* $MFT must be non-resident. */ + if (!a->non_resident) { + ntfs_log_error("$MFT must be non-resident.\n"); + goto io_error_exit; + } + /* $MFT must be uncompressed and unencrypted. */ + if (a->flags & ATTR_COMPRESSION_MASK || + a->flags & ATTR_IS_ENCRYPTED) { + ntfs_log_error("$MFT must be uncompressed and " + "unencrypted.\n"); + goto io_error_exit; + } + /* + * Decompress the mapping pairs array of this extent and merge + * the result into the existing runlist. No need for locking + * as we have exclusive access to the inode at this time and we + * are a mount in progress task, too. + */ + nrl = ntfs_mapping_pairs_decompress(vol, a, vol->mft_na->rl); + if (!nrl) { + ntfs_log_perror("ntfs_mapping_pairs_decompress() failed"); + goto error_exit; + } + vol->mft_na->rl = nrl; + + /* Get the lowest vcn for the next extent. */ + highest_vcn = sle64_to_cpu(a->highest_vcn); + next_vcn = highest_vcn + 1; + + /* Only one extent or error, which we catch below. */ + if (next_vcn <= 0) + break; + + /* Avoid endless loops due to corruption. */ + if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { + ntfs_log_error("$MFT has corrupt attribute list.\n"); + goto io_error_exit; + } + } + if (!a) { + ntfs_log_error("$MFT/$DATA attribute not found.\n"); + goto io_error_exit; + } + if (highest_vcn && highest_vcn != last_vcn - 1) { + ntfs_log_error("Failed to load runlist for $MFT/$DATA.\n"); + ntfs_log_error("highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx\n", + (long long)highest_vcn, (long long)last_vcn - 1); + goto io_error_exit; + } + /* Done with the $Mft mft record. */ + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + + /* Update the size fields in the inode. */ + vol->mft_ni->data_size = vol->mft_na->data_size; + vol->mft_ni->allocated_size = vol->mft_na->allocated_size; + set_nino_flag(vol->mft_ni, KnownSize); + + /* + * The volume is now setup so we can use all read access functions. + */ + vol->mftbmp_na = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0); + if (!vol->mftbmp_na) { + ntfs_log_perror("Failed to open $MFT/$BITMAP"); + goto error_exit; + } + return 0; +io_error_exit: + errno = EIO; +error_exit: + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (vol->mft_na) { + ntfs_attr_close(vol->mft_na); + vol->mft_na = NULL; + } + if (vol->mft_ni) { + ntfs_inode_close(vol->mft_ni); + vol->mft_ni = NULL; + } + errno = eo; + return -1; +} + +/** + * ntfs_mftmirr_load - load the $MFTMirr and setup the ntfs volume with it + * @vol: ntfs volume whose $MFTMirr to load + * + * Load $MFTMirr from @vol and setup @vol with it. After calling this function + * the volume @vol is ready for use by all write access functions provided by + * the ntfs library (assuming ntfs_mft_load() has been called successfully + * beforehand). + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_mftmirr_load(ntfs_volume *vol) +{ + int err; + + vol->mftmirr_ni = ntfs_inode_open(vol, FILE_MFTMirr); + if (!vol->mftmirr_ni) { + ntfs_log_perror("Failed to open inode $MFTMirr"); + return -1; + } + + vol->mftmirr_na = ntfs_attr_open(vol->mftmirr_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->mftmirr_na) { + ntfs_log_perror("Failed to open $MFTMirr/$DATA"); + goto error_exit; + } + + if (ntfs_attr_map_runlist(vol->mftmirr_na, 0) < 0) { + ntfs_log_perror("Failed to map runlist of $MFTMirr/$DATA"); + goto error_exit; + } + + return 0; + +error_exit: + err = errno; + if (vol->mftmirr_na) { + ntfs_attr_close(vol->mftmirr_na); + vol->mftmirr_na = NULL; + } + ntfs_inode_close(vol->mftmirr_ni); + vol->mftmirr_ni = NULL; + errno = err; + return -1; +} + +/** + * ntfs_volume_startup - allocate and setup an ntfs volume + * @dev: device to open + * @flags: optional mount flags + * + * Load, verify, and parse bootsector; load and setup $MFT and $MFTMirr. After + * calling this function, the volume is setup sufficiently to call all read + * and write access functions provided by the library. + * + * Return the allocated volume structure on success and NULL on error with + * errno set to the error code. + */ +ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, + ntfs_mount_flags flags) +{ + LCN mft_zone_size, mft_lcn; + s64 br; + ntfs_volume *vol; + NTFS_BOOT_SECTOR *bs; + int eo; + + if (!dev || !dev->d_ops || !dev->d_name) { + errno = EINVAL; + ntfs_log_perror("%s: dev = %p", __FUNCTION__, dev); + return NULL; + } + + bs = ntfs_malloc(sizeof(NTFS_BOOT_SECTOR)); + if (!bs) + return NULL; + + /* Allocate the volume structure. */ + vol = ntfs_volume_alloc(); + if (!vol) + goto error_exit; + + /* Create the default upcase table. */ + vol->upcase_len = ntfs_upcase_build_default(&vol->upcase); + if (!vol->upcase_len || !vol->upcase) + goto error_exit; + + /* Default with no locase table and case sensitive file names */ + vol->locase = (ntfschar*)NULL; + NVolSetCaseSensitive(vol); + + /* by default, all files are shown and not marked hidden */ + NVolSetShowSysFiles(vol); + NVolSetShowHidFiles(vol); + NVolClearHideDotFiles(vol); + /* set default compression */ +#if DEFAULT_COMPRESSION + NVolSetCompression(vol); +#else + NVolClearCompression(vol); +#endif + if (flags & NTFS_MNT_RDONLY) + NVolSetReadOnly(vol); + + /* ...->open needs bracketing to compile with glibc 2.7 */ + if ((dev->d_ops->open)(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) { + if (!NVolReadOnly(vol) && (errno == EROFS)) { + if ((dev->d_ops->open)(dev, O_RDONLY)) { + ntfs_log_perror("Error opening read-only '%s'", + dev->d_name); + goto error_exit; + } else { + ntfs_log_info("Can only open '%s' as read-only\n", + dev->d_name); + NVolSetReadOnly(vol); + } + } else { + ntfs_log_perror("Error opening '%s'", dev->d_name); + goto error_exit; + } + } + /* Attach the device to the volume. */ + vol->dev = dev; + + /* Now read the bootsector. */ + br = ntfs_pread(dev, 0, sizeof(NTFS_BOOT_SECTOR), bs); + if (br != sizeof(NTFS_BOOT_SECTOR)) { + if (br != -1) + errno = EINVAL; + if (!br) + ntfs_log_error("Failed to read bootsector (size=0)\n"); + else + ntfs_log_perror("Error reading bootsector"); + goto error_exit; + } + if (!ntfs_boot_sector_is_ntfs(bs)) { + errno = EINVAL; + goto error_exit; + } + if (ntfs_boot_sector_parse(vol, bs) < 0) + goto error_exit; + + free(bs); + bs = NULL; + /* Now set the device block size to the sector size. */ + if (ntfs_device_block_size_set(vol->dev, vol->sector_size)) + ntfs_log_debug("Failed to set the device block size to the " + "sector size. This may affect performance " + "but should be harmless otherwise. Error: " + "%s\n", strerror(errno)); + + /* We now initialize the cluster allocator. */ + vol->full_zones = 0; + mft_zone_size = vol->nr_clusters >> 3; /* 12.5% */ + + /* Setup the mft zone. */ + vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn; + ntfs_log_debug("mft_zone_pos = 0x%llx\n", (long long)vol->mft_zone_pos); + + /* + * Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs + * source) and if the actual mft_lcn is in the expected place or even + * further to the front of the volume, extend the mft_zone to cover the + * beginning of the volume as well. This is in order to protect the + * area reserved for the mft bitmap as well within the mft_zone itself. + * On non-standard volumes we don't protect it as the overhead would be + * higher than the speed increase we would get by doing it. + */ + mft_lcn = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size; + if (mft_lcn * vol->cluster_size < 16 * 1024) + mft_lcn = (16 * 1024 + vol->cluster_size - 1) / + vol->cluster_size; + if (vol->mft_zone_start <= mft_lcn) + vol->mft_zone_start = 0; + ntfs_log_debug("mft_zone_start = 0x%llx\n", (long long)vol->mft_zone_start); + + /* + * Need to cap the mft zone on non-standard volumes so that it does + * not point outside the boundaries of the volume. We do this by + * halving the zone size until we are inside the volume. + */ + vol->mft_zone_end = vol->mft_lcn + mft_zone_size; + while (vol->mft_zone_end >= vol->nr_clusters) { + mft_zone_size >>= 1; + vol->mft_zone_end = vol->mft_lcn + mft_zone_size; + } + ntfs_log_debug("mft_zone_end = 0x%llx\n", (long long)vol->mft_zone_end); + + /* + * Set the current position within each data zone to the start of the + * respective zone. + */ + vol->data1_zone_pos = vol->mft_zone_end; + ntfs_log_debug("data1_zone_pos = %lld\n", (long long)vol->data1_zone_pos); + vol->data2_zone_pos = 0; + ntfs_log_debug("data2_zone_pos = %lld\n", (long long)vol->data2_zone_pos); + + /* Set the mft data allocation position to mft record 24. */ + vol->mft_data_pos = 24; + + /* + * The cluster allocator is now fully operational. + */ + + /* Need to setup $MFT so we can use the library read functions. */ + if (ntfs_mft_load(vol) < 0) { + ntfs_log_perror("Failed to load $MFT"); + goto error_exit; + } + + /* Need to setup $MFTMirr so we can use the write functions, too. */ + if (ntfs_mftmirr_load(vol) < 0) { + ntfs_log_perror("Failed to load $MFTMirr"); + goto error_exit; + } + return vol; +error_exit: + eo = errno; + free(bs); + if (vol) + __ntfs_volume_release(vol); + errno = eo; + return NULL; +} + +/** + * ntfs_volume_check_logfile - check logfile on target volume + * @vol: volume on which to check logfile + * + * Return 0 on success and -1 on error with errno set error code. + */ +static int ntfs_volume_check_logfile(ntfs_volume *vol) +{ + ntfs_inode *ni; + ntfs_attr *na = NULL; + RESTART_PAGE_HEADER *rp = NULL; + int err = 0; + + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_LogFile"); + errno = EIO; + return -1; + } + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); + err = EIO; + goto out; + } + + if (!ntfs_check_logfile(na, &rp) || !ntfs_is_logfile_clean(na, rp)) + err = EOPNOTSUPP; + /* + * If the latest restart page was identified as version + * 2.0, then Windows may have kept a cached copy of + * metadata for fast restarting, and we should not mount. + * Hibernation will be seen the same way on a non + * Windows-system partition, so we have to use the same + * error code (EPERM). + * The restart page may also be identified as version 2.0 + * when access to the file system is terminated abruptly + * by unplugging or power cut, so mounting is also rejected + * after such an event. + */ + if (rp + && (rp->major_ver == const_cpu_to_le16(2)) + && (rp->minor_ver == const_cpu_to_le16(0))) { + ntfs_log_error("Metadata kept in Windows cache, refused to mount.\n"); + err = EPERM; + } + free(rp); + ntfs_attr_close(na); +out: + if (ntfs_inode_close(ni)) + ntfs_error_set(&err); + if (err) { + errno = err; + return -1; + } + return 0; +} + +/** + * ntfs_hiberfile_open - Find and open '/hiberfil.sys' + * @vol: An ntfs volume obtained from ntfs_mount + * + * Return: inode Success, hiberfil.sys is valid + * NULL hiberfil.sys doesn't exist or some other error occurred + */ +static ntfs_inode *ntfs_hiberfile_open(ntfs_volume *vol) +{ + u64 inode; + ntfs_inode *ni_root; + ntfs_inode *ni_hibr = NULL; + ntfschar *unicode = NULL; + int unicode_len; + const char *hiberfile = "hiberfil.sys"; + + if (!vol) { + errno = EINVAL; + return NULL; + } + + ni_root = ntfs_inode_open(vol, FILE_root); + if (!ni_root) { + ntfs_log_debug("Couldn't open the root directory.\n"); + return NULL; + } + + unicode_len = ntfs_mbstoucs(hiberfile, &unicode); + if (unicode_len < 0) { + ntfs_log_perror("Couldn't convert 'hiberfil.sys' to Unicode"); + goto out; + } + + inode = ntfs_inode_lookup_by_name(ni_root, unicode, unicode_len); + if (inode == (u64)-1) { + ntfs_log_debug("Couldn't find file '%s'.\n", hiberfile); + goto out; + } + + inode = MREF(inode); + ni_hibr = ntfs_inode_open(vol, inode); + if (!ni_hibr) { + ntfs_log_debug("Couldn't open inode %lld.\n", (long long)inode); + goto out; + } +out: + if (ntfs_inode_close(ni_root)) { + ntfs_inode_close(ni_hibr); + ni_hibr = NULL; + } + free(unicode); + return ni_hibr; +} + + +#define NTFS_HIBERFILE_HEADER_SIZE 4096 + +/** + * ntfs_volume_check_hiberfile - check hiberfil.sys whether Windows is + * hibernated on the target volume + * @vol: volume on which to check hiberfil.sys + * + * Return: 0 if Windows isn't hibernated for sure + * -1 otherwise and errno is set to the appropriate value + */ +int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose) +{ + ntfs_inode *ni; + ntfs_attr *na = NULL; + int bytes_read, err; + char *buf = NULL; + + ni = ntfs_hiberfile_open(vol); + if (!ni) { + if (errno == ENOENT) + return 0; + return -1; + } + + buf = ntfs_malloc(NTFS_HIBERFILE_HEADER_SIZE); + if (!buf) + goto out; + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open hiberfil.sys data attribute"); + goto out; + } + + bytes_read = ntfs_attr_pread(na, 0, NTFS_HIBERFILE_HEADER_SIZE, buf); + if (bytes_read == -1) { + ntfs_log_perror("Failed to read hiberfil.sys"); + goto out; + } + if (bytes_read < NTFS_HIBERFILE_HEADER_SIZE) { + if (verbose) + ntfs_log_error("Hibernated non-system partition, " + "refused to mount.\n"); + errno = EPERM; + goto out; + } + if ((memcmp(buf, "hibr", 4) == 0) + || (memcmp(buf, "HIBR", 4) == 0)) { + if (verbose) + ntfs_log_error("Windows is hibernated, refused to mount.\n"); + errno = EPERM; + goto out; + } + /* All right, all header bytes are zero */ + errno = 0; +out: + if (na) + ntfs_attr_close(na); + free(buf); + err = errno; + if (ntfs_inode_close(ni)) + ntfs_error_set(&err); + errno = err; + return errno ? -1 : 0; +} + +/* + * Make sure a LOGGED_UTILITY_STREAM attribute named "$TXF_DATA" + * on the root directory is resident. + * When it is non-resident, the partition cannot be mounted on Vista + * (see http://support.microsoft.com/kb/974729) + * + * We take care to avoid this situation, however this can be a + * consequence of having used an older version (including older + * Windows version), so we had better fix it. + * + * Returns 0 if unneeded or successful + * -1 if there was an error, explained by errno + */ + +static int fix_txf_data(ntfs_volume *vol) +{ + void *txf_data; + s64 txf_data_size; + ntfs_inode *ni; + ntfs_attr *na; + int res; + + res = 0; + ntfs_log_debug("Loading root directory\n"); + ni = ntfs_inode_open(vol, FILE_root); + if (!ni) { + ntfs_log_perror("Failed to open root directory"); + res = -1; + } else { + /* Get the $TXF_DATA attribute */ + na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, TXF_DATA, 9); + if (na) { + if (NAttrNonResident(na)) { + /* + * Fix the attribute by truncating, then + * rewriting it. + */ + ntfs_log_debug("Making $TXF_DATA resident\n"); + txf_data = ntfs_attr_readall(ni, + AT_LOGGED_UTILITY_STREAM, + TXF_DATA, 9, &txf_data_size); + if (txf_data) { + if (ntfs_attr_truncate(na, 0) + || (ntfs_attr_pwrite(na, 0, + txf_data_size, txf_data) + != txf_data_size)) + res = -1; + free(txf_data); + } + if (res) + ntfs_log_error("Failed to make $TXF_DATA resident\n"); + else + ntfs_log_error("$TXF_DATA made resident\n"); + } + ntfs_attr_close(na); + } + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close root"); + res = -1; + } + } + return (res); +} + +/** + * ntfs_device_mount - open ntfs volume + * @dev: device to open + * @flags: optional mount flags + * + * This function mounts an ntfs volume. @dev should describe the device which + * to mount as the ntfs volume. + * + * @flags is an optional second parameter. The same flags are used as for + * the mount system call (man 2 mount). Currently only the following flag + * is implemented: + * NTFS_MNT_RDONLY - mount volume read-only + * + * The function opens the device @dev and verifies that it contains a valid + * bootsector. Then, it allocates an ntfs_volume structure and initializes + * some of the values inside the structure from the information stored in the + * bootsector. It proceeds to load the necessary system files and completes + * setting up the structure. + * + * Return the allocated volume structure on success and NULL on error with + * errno set to the error code. + */ +ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) +{ + s64 l; + ntfs_volume *vol; + u8 *m = NULL, *m2 = NULL; + ntfs_attr_search_ctx *ctx = NULL; + ntfs_inode *ni; + ntfs_attr *na; + ATTR_RECORD *a; + VOLUME_INFORMATION *vinf; + ntfschar *vname; + int i, j, eo; + unsigned int k; + u32 u; + + vol = ntfs_volume_startup(dev, flags); + if (!vol) + return NULL; + + /* Load data from $MFT and $MFTMirr and compare the contents. */ + m = ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); + m2 = ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); + if (!m || !m2) + goto error_exit; + + l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, + vol->mft_record_size, m); + if (l != vol->mftmirr_size) { + if (l == -1) + ntfs_log_perror("Failed to read $MFT"); + else { + ntfs_log_error("Failed to read $MFT, unexpected length " + "(%lld != %d).\n", (long long)l, + vol->mftmirr_size); + errno = EIO; + } + goto error_exit; + } + l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, + vol->mft_record_size, m2); + if (l != vol->mftmirr_size) { + if (l == -1) { + ntfs_log_perror("Failed to read $MFTMirr"); + goto error_exit; + } + vol->mftmirr_size = l; + } + ntfs_log_debug("Comparing $MFTMirr to $MFT...\n"); + for (i = 0; i < vol->mftmirr_size; ++i) { + MFT_RECORD *mrec, *mrec2; + const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", + "$Volume", "$AttrDef", "root directory", "$Bitmap", + "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; + const char *s; + + if (i < 12) + s = ESTR[i]; + else if (i < 16) + s = "system file"; + else + s = "mft record"; + + mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); + if (mrec->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp(mrec)) { + ntfs_log_error("$MFT error: Incomplete multi " + "sector transfer detected in " + "'%s'.\n", s); + goto io_error_exit; + } + if (!ntfs_is_mft_recordp(mrec)) { + ntfs_log_error("$MFT error: Invalid mft " + "record for '%s'.\n", s); + goto io_error_exit; + } + } + mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); + if (mrec2->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_recordp(mrec2)) { + ntfs_log_error("$MFTMirr error: Incomplete " + "multi sector transfer " + "detected in '%s'.\n", s); + goto io_error_exit; + } + if (!ntfs_is_mft_recordp(mrec2)) { + ntfs_log_error("$MFTMirr error: Invalid mft " + "record for '%s'.\n", s); + goto io_error_exit; + } + } + if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { + ntfs_log_error("$MFTMirr does not match $MFT (record " + "%d).\n", i); + goto io_error_exit; + } + } + + free(m2); + free(m); + m = m2 = NULL; + + /* Now load the bitmap from $Bitmap. */ + ntfs_log_debug("Loading $Bitmap...\n"); + vol->lcnbmp_ni = ntfs_inode_open(vol, FILE_Bitmap); + if (!vol->lcnbmp_ni) { + ntfs_log_perror("Failed to open inode FILE_Bitmap"); + goto error_exit; + } + + vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->lcnbmp_na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + + if (vol->lcnbmp_na->data_size > vol->lcnbmp_na->allocated_size) { + ntfs_log_error("Corrupt cluster map size (%lld > %lld)\n", + (long long)vol->lcnbmp_na->data_size, + (long long)vol->lcnbmp_na->allocated_size); + goto io_error_exit; + } + + /* Now load the upcase table from $UpCase. */ + ntfs_log_debug("Loading $UpCase...\n"); + ni = ntfs_inode_open(vol, FILE_UpCase); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_UpCase"); + goto error_exit; + } + /* Get an ntfs attribute for $UpCase/$DATA. */ + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* + * Note: Normally, the upcase table has a length equal to 65536 + * 2-byte Unicode characters but allow for different cases, so no + * checks done. Just check we don't overflow 32-bits worth of Unicode + * characters. + */ + if (na->data_size & ~0x1ffffffffULL) { + ntfs_log_error("Error: Upcase table is too big (max 32-bit " + "allowed).\n"); + errno = EINVAL; + goto error_exit; + } + if (vol->upcase_len != na->data_size >> 1) { + vol->upcase_len = na->data_size >> 1; + /* Throw away default table. */ + free(vol->upcase); + vol->upcase = ntfs_malloc(na->data_size); + if (!vol->upcase) + goto error_exit; + } + /* Read in the $DATA attribute value into the buffer. */ + l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase); + if (l != na->data_size) { + ntfs_log_error("Failed to read $UpCase, unexpected length " + "(%lld != %lld).\n", (long long)l, + (long long)na->data_size); + errno = EIO; + goto error_exit; + } + /* Done with the $UpCase mft record. */ + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close $UpCase"); + goto error_exit; + } + /* Consistency check of $UpCase, restricted to plain ASCII chars */ + k = 0x20; + while ((k < vol->upcase_len) + && (k < 0x7f) + && (le16_to_cpu(vol->upcase[k]) + == ((k < 'a') || (k > 'z') ? k : k + 'A' - 'a'))) + k++; + if (k < 0x7f) { + ntfs_log_error("Corrupted file $UpCase\n"); + goto io_error_exit; + } + + /* + * Now load $Volume and set the version information and flags in the + * vol structure accordingly. + */ + ntfs_log_debug("Loading $Volume...\n"); + vol->vol_ni = ntfs_inode_open(vol, FILE_Volume); + if (!vol->vol_ni) { + ntfs_log_perror("Failed to open inode FILE_Volume"); + goto error_exit; + } + /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */ + ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); + if (!ctx) + goto error_exit; + + /* Find the $VOLUME_INFORMATION attribute. */ + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, + 0, ctx)) { + ntfs_log_perror("$VOLUME_INFORMATION attribute not found in " + "$Volume"); + goto error_exit; + } + a = ctx->attr; + /* Has to be resident. */ + if (a->non_resident) { + ntfs_log_error("Attribute $VOLUME_INFORMATION must be " + "resident but it isn't.\n"); + errno = EIO; + goto error_exit; + } + /* Get a pointer to the value of the attribute. */ + vinf = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); + /* Sanity checks. */ + if ((char*)vinf + le32_to_cpu(a->value_length) > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_in_use) || + le16_to_cpu(a->value_offset) + le32_to_cpu( + a->value_length) > le32_to_cpu(a->length)) { + ntfs_log_error("$VOLUME_INFORMATION in $Volume is corrupt.\n"); + errno = EIO; + goto error_exit; + } + /* Setup vol from the volume information attribute value. */ + vol->major_ver = vinf->major_ver; + vol->minor_ver = vinf->minor_ver; + /* Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are + defined using cpu_to_le16() macro and hence are consistent. */ + vol->flags = vinf->flags; + /* + * Reinitialize the search context for the $Volume/$VOLUME_NAME lookup. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + if (errno != ENOENT) { + ntfs_log_perror("Failed to lookup of $VOLUME_NAME in " + "$Volume failed"); + goto error_exit; + } + /* + * Attribute not present. This has been seen in the field. + * Treat this the same way as if the attribute was present but + * had zero length. + */ + vol->vol_name = ntfs_malloc(1); + if (!vol->vol_name) + goto error_exit; + vol->vol_name[0] = '\0'; + } else { + a = ctx->attr; + /* Has to be resident. */ + if (a->non_resident) { + ntfs_log_error("$VOLUME_NAME must be resident.\n"); + errno = EIO; + goto error_exit; + } + /* Get a pointer to the value of the attribute. */ + vname = (ntfschar*)(le16_to_cpu(a->value_offset) + (char*)a); + u = le32_to_cpu(a->value_length) / 2; + /* + * Convert Unicode volume name to current locale multibyte + * format. + */ + vol->vol_name = NULL; + if (ntfs_ucstombs(vname, u, &vol->vol_name, 0) == -1) { + ntfs_log_perror("Volume name could not be converted " + "to current locale"); + ntfs_log_debug("Forcing name into ASCII by replacing " + "non-ASCII characters with underscores.\n"); + vol->vol_name = ntfs_malloc(u + 1); + if (!vol->vol_name) + goto error_exit; + + for (j = 0; j < (s32)u; j++) { + u16 uc = le16_to_cpu(vname[j]); + if (uc > 0xff) + uc = (u16)'_'; + vol->vol_name[j] = (char)uc; + } + vol->vol_name[u] = '\0'; + } + } + ntfs_attr_put_search_ctx(ctx); + ctx = NULL; + /* Now load the attribute definitions from $AttrDef. */ + ntfs_log_debug("Loading $AttrDef...\n"); + ni = ntfs_inode_open(vol, FILE_AttrDef); + if (!ni) { + ntfs_log_perror("Failed to open $AttrDef"); + goto error_exit; + } + /* Get an ntfs attribute for $AttrDef/$DATA. */ + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* Check we don't overflow 32-bits. */ + if (na->data_size > 0xffffffffLL) { + ntfs_log_error("Attribute definition table is too big (max " + "32-bit allowed).\n"); + errno = EINVAL; + goto error_exit; + } + vol->attrdef_len = na->data_size; + vol->attrdef = ntfs_malloc(na->data_size); + if (!vol->attrdef) + goto error_exit; + /* Read in the $DATA attribute value into the buffer. */ + l = ntfs_attr_pread(na, 0, na->data_size, vol->attrdef); + if (l != na->data_size) { + ntfs_log_error("Failed to read $AttrDef, unexpected length " + "(%lld != %lld).\n", (long long)l, + (long long)na->data_size); + errno = EIO; + goto error_exit; + } + /* Done with the $AttrDef mft record. */ + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close $AttrDef"); + goto error_exit; + } + /* + * Check for dirty logfile and hibernated Windows. + * We care only about read-write mounts. + */ + if (!(flags & (NTFS_MNT_RDONLY | NTFS_MNT_FORENSIC))) { + if (!(flags & NTFS_MNT_IGNORE_HIBERFILE) && + ntfs_volume_check_hiberfile(vol, 1) < 0) + goto error_exit; + if (ntfs_volume_check_logfile(vol) < 0) { + /* Always reject cached metadata for now */ + if (!(flags & NTFS_MNT_RECOVER) || (errno == EPERM)) + goto error_exit; + ntfs_log_info("The file system wasn't safely " + "closed on Windows. Fixing.\n"); + if (ntfs_logfile_reset(vol)) + goto error_exit; + } + /* make $TXF_DATA resident if present on the root directory */ + if (fix_txf_data(vol)) + goto error_exit; + } + + return vol; +io_error_exit: + errno = EIO; +error_exit: + eo = errno; + if (ctx) + ntfs_attr_put_search_ctx(ctx); + free(m); + free(m2); + __ntfs_volume_release(vol); + errno = eo; + return NULL; +} + +/* + * Set appropriate flags for showing NTFS metafiles + * or files marked as hidden. + * Not set in ntfs_mount() to avoid breaking existing tools. + */ + +int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, + BOOL hide_dot_files) +{ + int res; + + res = -1; + if (vol) { + NVolClearShowSysFiles(vol); + NVolClearShowHidFiles(vol); + NVolClearHideDotFiles(vol); + if (show_sys_files) + NVolSetShowSysFiles(vol); + if (show_hid_files) + NVolSetShowHidFiles(vol); + if (hide_dot_files) + NVolSetHideDotFiles(vol); + res = 0; + } + if (res) + ntfs_log_error("Failed to set file visibility\n"); + return (res); +} + +/* + * Set ignore case mode + */ + +int ntfs_set_ignore_case(ntfs_volume *vol) +{ + int res; + + res = -1; + if (vol && vol->upcase) { + vol->locase = ntfs_locase_table_build(vol->upcase, + vol->upcase_len); + if (vol->locase) { + NVolClearCaseSensitive(vol); + res = 0; + } + } + if (res) + ntfs_log_error("Failed to set ignore_case mode\n"); + return (res); +} + +/** + * ntfs_mount - open ntfs volume + * @name: name of device/file to open + * @flags: optional mount flags + * + * This function mounts an ntfs volume. @name should contain the name of the + * device/file to mount as the ntfs volume. + * + * @flags is an optional second parameter. The same flags are used as for + * the mount system call (man 2 mount). Currently only the following flags + * is implemented: + * NTFS_MNT_RDONLY - mount volume read-only + * + * The function opens the device or file @name and verifies that it contains a + * valid bootsector. Then, it allocates an ntfs_volume structure and initializes + * some of the values inside the structure from the information stored in the + * bootsector. It proceeds to load the necessary system files and completes + * setting up the structure. + * + * Return the allocated volume structure on success and NULL on error with + * errno set to the error code. + * + * Note, that a copy is made of @name, and hence it can be discarded as + * soon as the function returns. + */ +ntfs_volume *ntfs_mount(const char *name __attribute__((unused)), + ntfs_mount_flags flags __attribute__((unused))) +{ +#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS + struct ntfs_device *dev; + ntfs_volume *vol; + + /* Allocate an ntfs_device structure. */ + dev = ntfs_device_alloc(name, 0, &ntfs_device_default_io_ops, NULL); + if (!dev) + return NULL; + /* Call ntfs_device_mount() to do the actual mount. */ + vol = ntfs_device_mount(dev, flags); + if (!vol) { + int eo = errno; + ntfs_device_free(dev); + errno = eo; + } else + ntfs_create_lru_caches(vol); + return vol; +#else + /* + * ntfs_mount() makes no sense if NO_NTFS_DEVICE_DEFAULT_IO_OPS is + * defined as there are no device operations available in libntfs in + * this case. + */ + errno = EOPNOTSUPP; + return NULL; +#endif +} + +/** + * ntfs_umount - close ntfs volume + * @vol: address of ntfs_volume structure of volume to close + * @force: if true force close the volume even if it is busy + * + * Deallocate all structures (including @vol itself) associated with the ntfs + * volume @vol. + * + * Return 0 on success. On error return -1 with errno set appropriately + * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that + * an operation is in progress and if you try the close later the operation + * might be completed and the close succeed. + * + * If @force is true (i.e. not zero) this function will close the volume even + * if this means that data might be lost. + * + * @vol must have previously been returned by a call to ntfs_mount(). + * + * @vol itself is deallocated and should no longer be dereferenced after this + * function returns success. If it returns an error then nothing has been done + * so it is safe to continue using @vol. + */ +int ntfs_umount(ntfs_volume *vol, const BOOL force __attribute__((unused))) +{ + struct ntfs_device *dev; + int ret; + + if (!vol) { + errno = EINVAL; + return -1; + } + dev = vol->dev; + ret = __ntfs_volume_release(vol); + ntfs_device_free(dev); + return ret; +} + +#ifdef HAVE_MNTENT_H + +/** + * ntfs_mntent_check - desc + * + * If you are wanting to use this, you actually wanted to use + * ntfs_check_if_mounted(), you just didn't realize. (-: + * + * See description of ntfs_check_if_mounted(), below. + */ +static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) +{ + struct mntent *mnt; + char *real_file = NULL, *real_fsname = NULL; + FILE *f; + int err = 0; + + real_file = ntfs_malloc(PATH_MAX + 1); + if (!real_file) + return -1; + real_fsname = ntfs_malloc(PATH_MAX + 1); + if (!real_fsname) { + err = errno; + goto exit; + } + if (!ntfs_realpath_canonicalize(file, real_file)) { + err = errno; + goto exit; + } + f = setmntent("/proc/mounts", "r"); + if (!f && !(f = setmntent(MOUNTED, "r"))) { + err = errno; + goto exit; + } + while ((mnt = getmntent(f))) { + if (!ntfs_realpath_canonicalize(mnt->mnt_fsname, real_fsname)) + continue; + if (!strcmp(real_file, real_fsname)) + break; + } + endmntent(f); + if (!mnt) + goto exit; + *mnt_flags = NTFS_MF_MOUNTED; + if (!strcmp(mnt->mnt_dir, "/")) + *mnt_flags |= NTFS_MF_ISROOT; +#ifdef HAVE_HASMNTOPT + if (hasmntopt(mnt, "ro") && !hasmntopt(mnt, "rw")) + *mnt_flags |= NTFS_MF_READONLY; +#endif +exit: + free(real_file); + free(real_fsname); + if (err) { + errno = err; + return -1; + } + return 0; +} + +#else /* HAVE_MNTENT_H */ + +#if defined(__sun) && defined (__SVR4) + +static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) +{ + struct mnttab *mnt = NULL; + char *real_file = NULL, *real_fsname = NULL; + FILE *f; + int err = 0; + + real_file = (char*)ntfs_malloc(PATH_MAX + 1); + if (!real_file) + return -1; + real_fsname = (char*)ntfs_malloc(PATH_MAX + 1); + mnt = (struct mnttab*)ntfs_malloc(MNT_LINE_MAX + 1); + if (!real_fsname || !mnt) { + err = errno; + goto exit; + } + if (!ntfs_realpath_canonicalize(file, real_file)) { + err = errno; + goto exit; + } + if (!(f = fopen(MNTTAB, "r"))) { + err = errno; + goto exit; + } + while (!getmntent(f, mnt)) { + if (!ntfs_realpath_canonicalize(mnt->mnt_special, real_fsname)) + continue; + if (!strcmp(real_file, real_fsname)) { + *mnt_flags = NTFS_MF_MOUNTED; + if (!strcmp(mnt->mnt_mountp, "/")) + *mnt_flags |= NTFS_MF_ISROOT; + if (hasmntopt(mnt, "ro") && !hasmntopt(mnt, "rw")) + *mnt_flags |= NTFS_MF_READONLY; + break; + } + } + fclose(f); +exit: + free(mnt); + free(real_file); + free(real_fsname); + if (err) { + errno = err; + return -1; + } + return 0; +} + +#endif /* defined(__sun) && defined (__SVR4) */ +#endif /* HAVE_MNTENT_H */ + +/** + * ntfs_check_if_mounted - check if an ntfs volume is currently mounted + * @file: device file to check + * @mnt_flags: pointer into which to return the ntfs mount flags (see volume.h) + * + * If the running system does not support the {set,get,end}mntent() calls, + * just return 0 and set *@mnt_flags to zero. + * + * When the system does support the calls, ntfs_check_if_mounted() first tries + * to find the device @file in /etc/mtab (or wherever this is kept on the + * running system). If it is not found, assume the device is not mounted and + * return 0 and set *@mnt_flags to zero. + * + * If the device @file is found, set the NTFS_MF_MOUNTED flags in *@mnt_flags. + * + * Further if @file is mounted as the file system root ("/"), set the flag + * NTFS_MF_ISROOT in *@mnt_flags. + * + * Finally, check if the file system is mounted read-only, and if so set the + * NTFS_MF_READONLY flag in *@mnt_flags. + * + * On success return 0 with *@mnt_flags set to the ntfs mount flags. + * + * On error return -1 with errno set to the error code. + */ +int ntfs_check_if_mounted(const char *file __attribute__((unused)), + unsigned long *mnt_flags) +{ + *mnt_flags = 0; +#if defined(HAVE_MNTENT_H) || (defined(__sun) && defined (__SVR4)) + return ntfs_mntent_check(file, mnt_flags); +#else + return 0; +#endif +} + +/** + * ntfs_version_is_supported - check if NTFS version is supported. + * @vol: ntfs volume whose version we're interested in. + * + * The function checks if the NTFS volume version is known or not. + * Version 1.1 and 1.2 are used by Windows NT3.x and NT4. + * Version 2.x is used by Windows 2000 Betas. + * Version 3.0 is used by Windows 2000. + * Version 3.1 is used by Windows XP, Windows Server 2003 and Longhorn. + * + * Return 0 if NTFS version is supported otherwise -1 with errno set. + * + * The following error codes are defined: + * EOPNOTSUPP - Unknown NTFS version + * EINVAL - Invalid argument + */ +int ntfs_version_is_supported(ntfs_volume *vol) +{ + u8 major, minor; + + if (!vol) { + errno = EINVAL; + return -1; + } + + major = vol->major_ver; + minor = vol->minor_ver; + + if (NTFS_V1_1(major, minor) || NTFS_V1_2(major, minor)) + return 0; + + if (NTFS_V2_X(major, minor)) + return 0; + + if (NTFS_V3_0(major, minor) || NTFS_V3_1(major, minor)) + return 0; + + errno = EOPNOTSUPP; + return -1; +} + +/** + * ntfs_logfile_reset - "empty" $LogFile data attribute value + * @vol: ntfs volume whose $LogFile we intend to reset. + * + * Fill the value of the $LogFile data attribute, i.e. the contents of + * the file, with 0xff's, thus marking the journal as empty. + * + * FIXME(?): We might need to zero the LSN field of every single mft + * record as well. (But, first try without doing that and see what + * happens, since chkdsk might pickup the pieces and do it for us...) + * + * On success return 0. + * + * On error return -1 with errno set to the error code. + */ +int ntfs_logfile_reset(ntfs_volume *vol) +{ + ntfs_inode *ni; + ntfs_attr *na; + int eo; + + if (!vol) { + errno = EINVAL; + return -1; + } + + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_LogFile"); + return -1; + } + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + eo = errno; + ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); + goto error_exit; + } + + if (ntfs_empty_logfile(na)) { + eo = errno; + ntfs_attr_close(na); + goto error_exit; + } + + ntfs_attr_close(na); + return ntfs_inode_close(ni); + +error_exit: + ntfs_inode_close(ni); + errno = eo; + return -1; +} + +/** + * ntfs_volume_write_flags - set the flags of an ntfs volume + * @vol: ntfs volume where we set the volume flags + * @flags: new flags + * + * Set the on-disk volume flags in the mft record of $Volume and + * on volume @vol to @flags. + * + * Return 0 if successful and -1 if not with errno set to the error code. + */ +int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags) +{ + ATTR_RECORD *a; + VOLUME_INFORMATION *c; + ntfs_attr_search_ctx *ctx; + int ret = -1; /* failure */ + + if (!vol || !vol->vol_ni) { + errno = EINVAL; + return -1; + } + /* Get a pointer to the volume information attribute. */ + ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); + if (!ctx) + return -1; + + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, + 0, ctx)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION was not found " + "in $Volume!\n"); + goto err_out; + } + a = ctx->attr; + /* Sanity check. */ + if (a->non_resident) { + ntfs_log_error("Attribute $VOLUME_INFORMATION must be resident " + "but it isn't.\n"); + errno = EIO; + goto err_out; + } + /* Get a pointer to the value of the attribute. */ + c = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); + /* Sanity checks. */ + if ((char*)c + le32_to_cpu(a->value_length) > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_in_use) || + le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length) > le32_to_cpu(a->length)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION in $Volume is " + "corrupt!\n"); + errno = EIO; + goto err_out; + } + /* Set the volume flags. */ + vol->flags = c->flags = flags & VOLUME_FLAGS_MASK; + /* Write them to disk. */ + ntfs_inode_mark_dirty(vol->vol_ni); + if (ntfs_inode_sync(vol->vol_ni)) + goto err_out; + + ret = 0; /* success */ +err_out: + ntfs_attr_put_search_ctx(ctx); + return ret; +} + +int ntfs_volume_error(int err) +{ + int ret; + + switch (err) { + case 0: + ret = NTFS_VOLUME_OK; + break; + case EINVAL: + ret = NTFS_VOLUME_NOT_NTFS; + break; + case EIO: + ret = NTFS_VOLUME_CORRUPT; + break; + case EPERM: + /* + * Hibernation and fast restarting are seen the + * same way on a non Windows-system partition. + */ + ret = NTFS_VOLUME_HIBERNATED; + break; + case EOPNOTSUPP: + ret = NTFS_VOLUME_UNCLEAN_UNMOUNT; + break; + case EBUSY: + ret = NTFS_VOLUME_LOCKED; + break; + case ENXIO: + ret = NTFS_VOLUME_RAID; + break; + case EACCES: + ret = NTFS_VOLUME_NO_PRIVILEGE; + break; + default: + ret = NTFS_VOLUME_UNKNOWN_REASON; + break; + } + return ret; +} + + +void ntfs_mount_error(const char *volume, const char *mntpoint, int err) +{ + switch (err) { + case NTFS_VOLUME_NOT_NTFS: + ntfs_log_error(invalid_ntfs_msg, volume); + break; + case NTFS_VOLUME_CORRUPT: + ntfs_log_error("%s", corrupt_volume_msg); + break; + case NTFS_VOLUME_HIBERNATED: + ntfs_log_error(hibernated_volume_msg, volume, mntpoint); + break; + case NTFS_VOLUME_UNCLEAN_UNMOUNT: + ntfs_log_error("%s", unclean_journal_msg); + break; + case NTFS_VOLUME_LOCKED: + ntfs_log_error("%s", opened_volume_msg); + break; + case NTFS_VOLUME_RAID: + ntfs_log_error("%s", fakeraid_msg); + break; + case NTFS_VOLUME_NO_PRIVILEGE: + ntfs_log_error(access_denied_msg, volume); + break; + } +} + +int ntfs_set_locale(void) +{ + const char *locale; + + locale = setlocale(LC_ALL, ""); + if (!locale) { + locale = setlocale(LC_ALL, NULL); + ntfs_log_error("Couldn't set local environment, using default " + "'%s'.\n", locale); + return 1; + } + return 0; +} + +/* + * Feed the counts of free clusters and free mft records + */ + +int ntfs_volume_get_free_space(ntfs_volume *vol) +{ + ntfs_attr *na; + int ret; + + ret = -1; /* default return */ + vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na); + if (vol->free_clusters < 0) { + ntfs_log_perror("Failed to read NTFS $Bitmap"); + } else { + na = vol->mftbmp_na; + vol->free_mft_records = ntfs_attr_get_free_bits(na); + + if (vol->free_mft_records >= 0) + vol->free_mft_records += (na->allocated_size - na->data_size) << 3; + + if (vol->free_mft_records < 0) + ntfs_log_perror("Failed to calculate free MFT records"); + else + ret = 0; + } + return (ret); +} + +/** + * ntfs_volume_rename - change the current label on a volume + * @vol: volume to change the label on + * @label: the new label + * @label_len: the length of @label in ntfschars including the terminating NULL + * character, which is mandatory (the value can not exceed 128) + * + * Change the label on the volume @vol to @label. + */ +int ntfs_volume_rename(ntfs_volume *vol, const ntfschar *label, int label_len) +{ + ntfs_attr *na; + char *old_vol_name; + char *new_vol_name = NULL; + int new_vol_name_len; + int err; + + if (NVolReadOnly(vol)) { + ntfs_log_error("Refusing to change label on read-only mounted " + "volume.\n"); + errno = EROFS; + return -1; + } + + label_len *= sizeof(ntfschar); + if (label_len > 0x100) { + ntfs_log_error("New label is too long. Maximum %u characters " + "allowed.\n", + (unsigned)(0x100 / sizeof(ntfschar))); + errno = ERANGE; + return -1; + } + + na = ntfs_attr_open(vol->vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0); + if (!na) { + if (errno != ENOENT) { + err = errno; + ntfs_log_perror("Lookup of $VOLUME_NAME attribute " + "failed"); + goto err_out; + } + + /* The volume name attribute does not exist. Need to add it. */ + if (ntfs_attr_add(vol->vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0, + (const u8*) label, label_len)) + { + err = errno; + ntfs_log_perror("Encountered error while adding " + "$VOLUME_NAME attribute"); + goto err_out; + } + } + else { + s64 written; + + if (NAttrNonResident(na)) { + err = errno; + ntfs_log_error("Error: Attribute $VOLUME_NAME must be " + "resident.\n"); + goto err_out; + } + + if (na->data_size != label_len) { + if (ntfs_attr_truncate(na, label_len)) { + err = errno; + ntfs_log_perror("Error resizing resident " + "attribute"); + goto err_out; + } + } + + if (label_len) { + written = ntfs_attr_pwrite(na, 0, label_len, label); + if (written == -1) { + err = errno; + ntfs_log_perror("Error when writing " + "$VOLUME_NAME data"); + goto err_out; + } + else if (written != label_len) { + err = EIO; + ntfs_log_error("Partial write when writing " + "$VOLUME_NAME data."); + goto err_out; + + } + } + } + + new_vol_name_len = + ntfs_ucstombs(label, label_len, &new_vol_name, 0); + if (new_vol_name_len == -1) { + err = errno; + ntfs_log_perror("Error while decoding new volume name"); + goto err_out; + } + + old_vol_name = vol->vol_name; + vol->vol_name = new_vol_name; + free(old_vol_name); + + err = 0; +err_out: + if (na) + ntfs_attr_close(na); + if (err) + errno = err; + return err ? -1 : 0; +} diff --git a/libntfs-3g/win32_io.c b/libntfs-3g/win32_io.c new file mode 100755 index 0000000000000000000000000000000000000000..9c84ec677502d2a6c529039f44f53311cc92fd8b --- /dev/null +++ b/libntfs-3g/win32_io.c @@ -0,0 +1,2033 @@ +/* + * win32_io.c - A stdio-like disk I/O implementation for low-level disk access + * on Win32. Can access an NTFS volume while it is mounted. + * Originated from the Linux-NTFS project. + * + * Copyright (c) 2003-2004 Lode Leroy + * Copyright (c) 2003-2006 Anton Altaparmakov + * Copyright (c) 2004-2005 Yuval Fledel + * Copyright (c) 2012-2014 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_WINDOWS_H +#define BOOL WINBOOL /* avoid conflicting definitions of BOOL */ +#include <windows.h> +#undef BOOL +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +/* + * Definitions needed for <winioctl.h> + */ +#ifndef _ANONYMOUS_UNION +#define _ANONYMOUS_UNION +#define _ANONYMOUS_STRUCT +typedef unsigned long long DWORD64; +#endif + +typedef struct { + DWORD data1; /* The first eight hexadecimal digits of the GUID. */ + WORD data2; /* The first group of four hexadecimal digits. */ + WORD data3; /* The second group of four hexadecimal digits. */ + char data4[8]; /* The first two bytes are the third group of four + hexadecimal digits. The remaining six bytes are the + final 12 hexadecimal digits. */ +} GUID; + +#include <winioctl.h> + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#define stat stat64 +#define st_blocks st_rdev /* emulate st_blocks, missing in Windows */ +#endif + +/* Prevent volume.h from being be loaded, as it conflicts with winnt.h. */ +#define _NTFS_VOLUME_H +struct ntfs_volume; +typedef struct ntfs_volume ntfs_volume; + +#include "debug.h" +#include "types.h" +#include "device.h" +#include "misc.h" + +#define cpu_to_le16(x) (x) +#define const_cpu_to_le16(x) (x) + +#ifndef MAX_PATH +#define MAX_PATH 1024 +#endif + +#ifndef NTFS_BLOCK_SIZE +#define NTFS_BLOCK_SIZE 512 +#define NTFS_BLOCK_SIZE_BITS 9 +#endif + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +#ifndef IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS +#define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 5636096 +#endif + +#ifndef IOCTL_DISK_GET_DRIVE_GEOMETRY +#define IOCTL_DISK_GET_DRIVE_GEOMETRY 0x70000 +#endif + +#ifndef IOCTL_GET_DISK_LENGTH_INFO +#define IOCTL_GET_DISK_LENGTH_INFO 0x7405c +#endif + +#ifndef FSCTL_ALLOW_EXTENDED_DASD_IO +#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x90083 +#endif + +/* Windows 2k+ imports. */ +typedef HANDLE (WINAPI *LPFN_FINDFIRSTVOLUME)(LPTSTR, DWORD); +typedef BOOL (WINAPI *LPFN_FINDNEXTVOLUME)(HANDLE, LPTSTR, DWORD); +typedef BOOL (WINAPI *LPFN_FINDVOLUMECLOSE)(HANDLE); +typedef BOOL (WINAPI *LPFN_SETFILEPOINTEREX)(HANDLE, LARGE_INTEGER, + PLARGE_INTEGER, DWORD); + +static LPFN_FINDFIRSTVOLUME fnFindFirstVolume = NULL; +static LPFN_FINDNEXTVOLUME fnFindNextVolume = NULL; +static LPFN_FINDVOLUMECLOSE fnFindVolumeClose = NULL; +static LPFN_SETFILEPOINTEREX fnSetFilePointerEx = NULL; + +#ifdef UNICODE +#define FNPOSTFIX "W" +#else +#define FNPOSTFIX "A" +#endif + +enum { /* see http://msdn.microsoft.com/en-us/library/cc704588(v=prot.10).aspx */ + STATUS_UNKNOWN = -1, + STATUS_SUCCESS = 0x00000000, + STATUS_BUFFER_OVERFLOW = 0x80000005, + STATUS_INVALID_HANDLE = 0xC0000008, + STATUS_INVALID_PARAMETER = 0xC000000D, + STATUS_INVALID_DEVICE_REQUEST = 0xC0000010, + STATUS_END_OF_FILE = 0xC0000011, + STATUS_CONFLICTING_ADDRESSES = 0xC0000018, + STATUS_NO_MATCH = 0xC000001E, + STATUS_ACCESS_DENIED = 0xC0000022, + STATUS_BUFFER_TOO_SMALL = 0xC0000023, + STATUS_OBJECT_TYPE_MISMATCH = 0xC0000024, + STATUS_FILE_NOT_FOUND = 0xC0000028, + STATUS_OBJECT_NAME_INVALID = 0xC0000033, + STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034, + STATUS_INVALID_PARAMETER_1 = 0xC00000EF, + STATUS_IO_DEVICE_ERROR = 0xC0000185, + STATUS_GUARD_PAGE_VIOLATION = 0x80000001 + } ; + +typedef u32 NTSTATUS; /* do not let the compiler choose the size */ +#ifdef __x86_64__ +typedef unsigned long long ULONG_PTR; /* an integer the same size as a pointer */ +#else +typedef unsigned long ULONG_PTR; /* an integer the same size as a pointer */ +#endif + +HANDLE get_osfhandle(int); /* from msvcrt.dll */ + +/* + * A few needed definitions not included in <windows.h> + */ + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; +#ifdef __x86_64__ + u32 padding; +#endif + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; +#ifdef __x86_64__ + u32 padding1; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + u32 padding2; +#else + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; +#endif + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +#define FILE_OPEN 1 +#define FILE_CREATE 2 +#define FILE_OVERWRITE 4 +#define FILE_SYNCHRONOUS_IO_ALERT 0x10 +#define FILE_SYNCHRONOUS_IO_NONALERT 0x20 +#define OBJ_CASE_INSENSITIVE 0x40 + +typedef void (WINAPI *PIO_APC_ROUTINE)(void*, PIO_STATUS_BLOCK, ULONG); + +extern WINAPI NTSTATUS NtOpenFile( + PHANDLE FileHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG ShareAccess, + ULONG OpenOptions +); + +extern WINAPI NTSTATUS NtReadFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID Buffer, + ULONG Length, + PLARGE_INTEGER ByteOffset, + PULONG Key +); + +extern WINAPI NTSTATUS NtWriteFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + LPCVOID Buffer, + ULONG Length, + PLARGE_INTEGER ByteOffset, + PULONG Key +); + +extern NTSTATUS WINAPI NtClose( + HANDLE Handle +); + +extern NTSTATUS WINAPI NtDeviceIoControlFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG IoControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength +); + +extern NTSTATUS WINAPI NtFsControlFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG FsControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength +); + +/** + * struct win32_fd - + */ +typedef struct { + HANDLE handle; + s64 pos; /* Logical current position on the volume. */ + s64 part_start; + s64 part_length; + int part_hidden_sectors; + s64 geo_size, geo_cylinders; + s32 geo_sector_size; + s64 volume_size; + DWORD geo_sectors, geo_heads; + HANDLE vol_handle; + BOOL ntdll; +} win32_fd; + +/** + * ntfs_w32error_to_errno - convert a win32 error code to the unix one + * @w32error: the win32 error code + * + * Limited to a relatively small but useful number of codes. + */ +static int ntfs_w32error_to_errno(unsigned int w32error) +{ + ntfs_log_trace("Converting w32error 0x%x.\n",w32error); + switch (w32error) { + case ERROR_INVALID_FUNCTION: + return EBADRQC; + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_NAME: + return ENOENT; + case ERROR_TOO_MANY_OPEN_FILES: + return EMFILE; + case ERROR_ACCESS_DENIED: + return EACCES; + case ERROR_INVALID_HANDLE: + return EBADF; + case ERROR_NOT_ENOUGH_MEMORY: + return ENOMEM; + case ERROR_OUTOFMEMORY: + return ENOSPC; + case ERROR_INVALID_DRIVE: + case ERROR_BAD_UNIT: + return ENODEV; + case ERROR_WRITE_PROTECT: + return EROFS; + case ERROR_NOT_READY: + case ERROR_SHARING_VIOLATION: + return EBUSY; + case ERROR_BAD_COMMAND: + return EINVAL; + case ERROR_SEEK: + case ERROR_NEGATIVE_SEEK: + return ESPIPE; + case ERROR_NOT_SUPPORTED: + return EOPNOTSUPP; + case ERROR_BAD_NETPATH: + return ENOSHARE; + default: + /* generic message */ + return ENOMSG; + } +} + +static int ntfs_ntstatus_to_errno(NTSTATUS status) +{ + ntfs_log_trace("Converting w32error 0x%x.\n",w32error); + switch (status) { + case STATUS_INVALID_HANDLE : + case STATUS_INVALID_PARAMETER : + case STATUS_OBJECT_NAME_INVALID : + case STATUS_INVALID_DEVICE_REQUEST : + return (EINVAL); + case STATUS_ACCESS_DENIED : + return (EACCES); + case STATUS_IO_DEVICE_ERROR : + case STATUS_END_OF_FILE : + return (EIO); + default: + /* generic message */ + return ENOMSG; + } +} + +/** + * libntfs_SetFilePointerEx - emulation for SetFilePointerEx() + * + * We use this to emulate SetFilePointerEx() when it is not present. This can + * happen since SetFilePointerEx() only exists in Win2k+. + */ +static BOOL WINAPI libntfs_SetFilePointerEx(HANDLE hFile, + LARGE_INTEGER liDistanceToMove, + PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) +{ + liDistanceToMove.u.LowPart = SetFilePointer(hFile, + liDistanceToMove.u.LowPart, + &liDistanceToMove.u.HighPart, dwMoveMethod); + SetLastError(NO_ERROR); + if (liDistanceToMove.u.LowPart == INVALID_SET_FILE_POINTER && + GetLastError() != NO_ERROR) { + if (lpNewFilePointer) + lpNewFilePointer->QuadPart = -1; + return FALSE; + } + if (lpNewFilePointer) + lpNewFilePointer->QuadPart = liDistanceToMove.QuadPart; + return TRUE; +} + +/** + * ntfs_device_win32_init_imports - initialize the function pointers + * + * The Find*Volume and SetFilePointerEx functions exist only on win2k+, as such + * we cannot just staticly import them. + * + * This function initializes the imports if the functions do exist and in the + * SetFilePointerEx case, we emulate the function ourselves if it is not + * present. + * + * Note: The values are cached, do be afraid to run it more than once. + */ +static void ntfs_device_win32_init_imports(void) +{ + HMODULE kernel32 = GetModuleHandle("kernel32"); + if (!kernel32) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("kernel32.dll could not be imported.\n"); + } + if (!fnSetFilePointerEx) { + if (kernel32) + fnSetFilePointerEx = (LPFN_SETFILEPOINTEREX) + GetProcAddress(kernel32, + "SetFilePointerEx"); + /* + * If we did not get kernel32.dll or it is not Win2k+, emulate + * SetFilePointerEx(). + */ + if (!fnSetFilePointerEx) { + ntfs_log_debug("SetFilePointerEx() not found in " + "kernel32.dll: Enabling emulation.\n"); + fnSetFilePointerEx = libntfs_SetFilePointerEx; + } + } + /* Cannot do lookups if we could not get kernel32.dll... */ + if (!kernel32) + return; + if (!fnFindFirstVolume) + fnFindFirstVolume = (LPFN_FINDFIRSTVOLUME) + GetProcAddress(kernel32, "FindFirstVolume" + FNPOSTFIX); + if (!fnFindNextVolume) + fnFindNextVolume = (LPFN_FINDNEXTVOLUME) + GetProcAddress(kernel32, "FindNextVolume" + FNPOSTFIX); + if (!fnFindVolumeClose) + fnFindVolumeClose = (LPFN_FINDVOLUMECLOSE) + GetProcAddress(kernel32, "FindVolumeClose"); +} + +/** + * ntfs_device_unix_status_flags_to_win32 - convert unix->win32 open flags + * @flags: unix open status flags + * + * Supported flags are O_RDONLY, O_WRONLY and O_RDWR. + */ +static __inline__ int ntfs_device_unix_status_flags_to_win32(int flags) +{ + int win_mode; + + switch (flags & O_ACCMODE) { + case O_RDONLY: + win_mode = GENERIC_READ; + break; + case O_WRONLY: + win_mode = GENERIC_WRITE; + break; + case O_RDWR: + win_mode = GENERIC_READ | GENERIC_WRITE; + break; + default: + /* error */ + ntfs_log_trace("Unknown status flags.\n"); + win_mode = 0; + } + return win_mode; +} + + +/** + * ntfs_device_win32_simple_open_file - just open a file via win32 API + * @filename: name of the file to open + * @handle: pointer the a HANDLE in which to put the result + * @flags: unix open status flags + * @locking: will the function gain an exclusive lock on the file? + * + * Supported flags are O_RDONLY, O_WRONLY and O_RDWR. + * + * Return 0 if o.k. + * -1 if not, and errno set. In this case handle is trashed. + */ +static int ntfs_device_win32_simple_open_file(const char *filename, + HANDLE *handle, int flags, BOOL locking) +{ + *handle = CreateFile(filename, + ntfs_device_unix_status_flags_to_win32(flags), + locking ? 0 : (FILE_SHARE_WRITE | FILE_SHARE_READ), + NULL, (flags & O_CREAT ? OPEN_ALWAYS : OPEN_EXISTING), + 0, NULL); + if (*handle == INVALID_HANDLE_VALUE) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("CreateFile(%s) failed.\n", filename); + return -1; + } + return 0; +} + +/** + * ntfs_device_win32_lock - lock the volume + * @handle: a win32 HANDLE for a volume to lock + * + * Locking a volume means no one can access its contents. + * Exiting the process automatically unlocks the volume, except in old NT4s. + * + * Return 0 if o.k. + * -1 if not, and errno set. + */ +static int ntfs_device_win32_lock(HANDLE handle) +{ + DWORD i; + + if (!DeviceIoControl(handle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &i, + NULL)) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("Couldn't lock volume.\n"); + return -1; + } + ntfs_log_debug("Volume locked.\n"); + return 0; +} + +/** + * ntfs_device_win32_unlock - unlock the volume + * @handle: the win32 HANDLE which the volume was locked with + * + * Return 0 if o.k. + * -1 if not, and errno set. + */ +static int ntfs_device_win32_unlock(HANDLE handle) +{ + DWORD i; + + if (!DeviceIoControl(handle, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &i, + NULL)) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("Couldn't unlock volume.\n"); + return -1; + } + ntfs_log_debug("Volume unlocked.\n"); + return 0; +} + +static int ntfs_device_win32_setlock(HANDLE handle, ULONG code) +{ + IO_STATUS_BLOCK io_status; + NTSTATUS res; + + io_status.Status = STATUS_SUCCESS; + io_status.Information = 0; + res = NtFsControlFile(handle,(HANDLE)NULL, + (PIO_APC_ROUTINE)NULL,(void*)NULL, + &io_status, code, + (char*)NULL,0,(char*)NULL,0); + if (res != STATUS_SUCCESS) + errno = ntfs_ntstatus_to_errno(res); + return (res == STATUS_SUCCESS ? 0 : -1); +} + +/** + * ntfs_device_win32_dismount - dismount a volume + * @handle: a win32 HANDLE for a volume to dismount + * + * Dismounting means the system will refresh the volume in the first change it + * gets. Usefull after altering the file structures. + * The volume must be locked by the current process while dismounting. + * A side effect is that the volume is also unlocked, but you must not rely om + * this. + * + * Return 0 if o.k. + * -1 if not, and errno set. + */ +static int ntfs_device_win32_dismount(HANDLE handle) +{ + DWORD i; + + if (!DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, + &i, NULL)) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("Couldn't dismount volume.\n"); + return -1; + } + ntfs_log_debug("Volume dismounted.\n"); + return 0; +} + +/** + * ntfs_device_win32_getsize - get file size via win32 API + * @handle: pointer the file HANDLE obtained via open + * + * Only works on ordinary files. + * + * Return The file size if o.k. + * -1 if not, and errno set. + */ +static s64 ntfs_device_win32_getsize(HANDLE handle) +{ + LONG loword, hiword; + + SetLastError(NO_ERROR); + hiword = 0; + loword = SetFilePointer(handle, 0, &hiword, 2); + if ((loword == INVALID_SET_FILE_POINTER) + && (GetLastError() != NO_ERROR)) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("Couldn't get file size.\n"); + return -1; + } + return ((s64)hiword << 32) + (ULONG)loword; +} + +/** + * ntfs_device_win32_getdisklength - get disk size via win32 API + * @handle: pointer the file HANDLE obtained via open + * @argp: pointer to result buffer + * + * Only works on PhysicalDriveX type handles. + * + * Return The disk size if o.k. + * -1 if not, and errno set. + */ +static s64 ntfs_device_win32_getdisklength(HANDLE handle) +{ + GET_LENGTH_INFORMATION buf; + DWORD i; + + if (!DeviceIoControl(handle, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf, + sizeof(buf), &i, NULL)) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("Couldn't get disk length.\n"); + return -1; + } + ntfs_log_debug("Disk length: %lld.\n", buf.Length.QuadPart); + return buf.Length.QuadPart; +} + +/** + * ntfs_device_win32_getntfssize - get NTFS volume size via win32 API + * @handle: pointer the file HANDLE obtained via open + * @argp: pointer to result buffer + * + * Only works on NTFS volume handles. + * An annoying bug in windows is that an NTFS volume does not occupy the entire + * partition, namely not the last sector (which holds the backup boot sector, + * and normally not interesting). + * Use this function to get the length of the accessible space through a given + * volume handle. + * + * Return The volume size if o.k. + * -1 if not, and errno set. + */ +static s64 ntfs_device_win32_getntfssize(HANDLE handle) +{ + s64 rvl; +#ifdef FSCTL_GET_NTFS_VOLUME_DATA + DWORD i; + NTFS_VOLUME_DATA_BUFFER buf; + + if (!DeviceIoControl(handle, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &buf, + sizeof(buf), &i, NULL)) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("Couldn't get NTFS volume length.\n"); + return -1; + } + rvl = buf.NumberSectors.QuadPart * buf.BytesPerSector; + ntfs_log_debug("NTFS volume length: 0x%llx.\n", (long long)rvl); +#else + errno = EINVAL; + rvl = -1; +#endif + return rvl; +} + +/** + * ntfs_device_win32_getgeo - get CHS information of a drive + * @handle: an open handle to the PhysicalDevice + * @fd: a win_fd structure that will be filled + * + * Return 0 if o.k. + * -1 if not, and errno set. + * + * In Windows NT+: fills size, sectors, and cylinders and sets heads to -1. + * In Windows XP+: fills size, sectors, cylinders, and heads. + * + * Note: In pre XP, this requires write permission, even though nothing is + * actually written. + * + * If fails, sets sectors, cylinders, heads, and size to -1. + */ +static int ntfs_device_win32_getgeo(HANDLE handle, win32_fd *fd) +{ + DWORD i; + BOOL rvl; + BYTE b[sizeof(DISK_GEOMETRY) + sizeof(DISK_PARTITION_INFO) + + sizeof(DISK_DETECTION_INFO) + 512]; + + rvl = DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, + 0, &b, sizeof(b), &i, NULL); + if (rvl) { + ntfs_log_debug("GET_DRIVE_GEOMETRY_EX detected.\n"); + DISK_DETECTION_INFO *ddi = (PDISK_DETECTION_INFO) + (((PBYTE)(&((PDISK_GEOMETRY_EX)b)->Data)) + + (((PDISK_PARTITION_INFO) + (&((PDISK_GEOMETRY_EX)b)->Data))-> + SizeOfPartitionInfo)); + fd->geo_cylinders = ((DISK_GEOMETRY*)&b)->Cylinders.QuadPart; + fd->geo_sectors = ((DISK_GEOMETRY*)&b)->SectorsPerTrack; + fd->geo_size = ((DISK_GEOMETRY_EX*)&b)->DiskSize.QuadPart; + fd->geo_sector_size = NTFS_BLOCK_SIZE; + switch (ddi->DetectionType) { + case DetectInt13: + fd->geo_cylinders = ddi->Int13.MaxCylinders; + fd->geo_sectors = ddi->Int13.SectorsPerTrack; + fd->geo_heads = ddi->Int13.MaxHeads; + return 0; + case DetectExInt13: + fd->geo_cylinders = ddi->ExInt13.ExCylinders; + fd->geo_sectors = ddi->ExInt13.ExSectorsPerTrack; + fd->geo_heads = ddi->ExInt13.ExHeads; + return 0; + case DetectNone: + default: + break; + } + } else + fd->geo_heads = -1; + rvl = DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, + &b, sizeof(b), &i, NULL); + if (rvl) { + ntfs_log_debug("GET_DRIVE_GEOMETRY detected.\n"); + fd->geo_cylinders = ((DISK_GEOMETRY*)&b)->Cylinders.QuadPart; + fd->geo_sectors = ((DISK_GEOMETRY*)&b)->SectorsPerTrack; + fd->geo_size = fd->geo_cylinders * fd->geo_sectors * + ((DISK_GEOMETRY*)&b)->TracksPerCylinder * + ((DISK_GEOMETRY*)&b)->BytesPerSector; + fd->geo_sector_size = ((DISK_GEOMETRY*)&b)->BytesPerSector; + return 0; + } + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("Couldn't retrieve disk geometry.\n"); + fd->geo_cylinders = -1; + fd->geo_sectors = -1; + fd->geo_size = -1; + fd->geo_sector_size = NTFS_BLOCK_SIZE; + return -1; +} + +static int ntfs_device_win32_getntgeo(HANDLE handle, win32_fd *fd) +{ + DISK_GEOMETRY geo; + NTSTATUS st; + IO_STATUS_BLOCK status; + u64 bytes; + int res; + + res = -1; + fd->geo_cylinders = 0; + fd->geo_sectors = 0; + fd->geo_size = 1073741824; + fd->geo_sectors = fd->geo_size >> 9; + fd->geo_sector_size = NTFS_BLOCK_SIZE; + + st = NtDeviceIoControlFile(handle, (HANDLE)NULL, + (PIO_APC_ROUTINE)NULL, (void*)NULL, + &status, IOCTL_DISK_GET_DRIVE_GEOMETRY, (void*)NULL, 0, + (void*)&geo, sizeof(geo)); + if (st == STATUS_SUCCESS) { + /* over-estimate the (rounded) number of cylinders */ + fd->geo_cylinders = geo.Cylinders.QuadPart + 1; + fd->geo_sectors = fd->geo_cylinders + *geo.TracksPerCylinder*geo.SectorsPerTrack; + fd->geo_size = fd->geo_sectors*geo.BytesPerSector; + fd->geo_sector_size = geo.BytesPerSector; + res = 0; + /* try to get the exact sector count */ + st = NtDeviceIoControlFile(handle, (HANDLE)NULL, + (PIO_APC_ROUTINE)NULL, (void*)NULL, + &status, IOCTL_GET_DISK_LENGTH_INFO, + (void*)NULL, 0, + (void*)&bytes, sizeof(bytes)); + if (st == STATUS_SUCCESS) { + fd->geo_size = bytes; + fd->geo_sectors = bytes/geo.BytesPerSector; + } + } + return (res); +} + +/** + * ntfs_device_win32_open_file - open a file via win32 API + * @filename: name of the file to open + * @fd: pointer to win32 file device in which to put the result + * @flags: unix open status flags + * + * Return 0 if o.k. + * -1 if not, and errno set. + */ +static __inline__ int ntfs_device_win32_open_file(char *filename, win32_fd *fd, + int flags) +{ + HANDLE handle; + int mode; + + if (ntfs_device_win32_simple_open_file(filename, &handle, flags, + FALSE)) { + /* open error */ + return -1; + } + mode = flags & O_ACCMODE; + if ((mode == O_RDWR) || (mode == O_WRONLY)) { + DWORD bytes; + + /* try making sparse (but ignore errors) */ + DeviceIoControl(handle, FSCTL_SET_SPARSE, + (void*)NULL, 0, (void*)NULL, 0, + &bytes, (LPOVERLAPPED)NULL); + } + /* fill fd */ + fd->handle = handle; + fd->part_start = 0; + fd->part_length = ntfs_device_win32_getsize(handle); + fd->pos = 0; + fd->part_hidden_sectors = -1; + fd->geo_size = -1; /* used as a marker that this is a file */ + fd->vol_handle = INVALID_HANDLE_VALUE; + fd->geo_sector_size = 512; /* will be adjusted from the boot sector */ + fd->ntdll = FALSE; + return 0; +} + +/** + * ntfs_device_win32_open_drive - open a drive via win32 API + * @drive_id: drive to open + * @fd: pointer to win32 file device in which to put the result + * @flags: unix open status flags + * + * return 0 if o.k. + * -1 if not, and errno set. + */ +static __inline__ int ntfs_device_win32_open_drive(int drive_id, win32_fd *fd, + int flags) +{ + HANDLE handle; + int err; + char filename[MAX_PATH]; + + sprintf(filename, "\\\\.\\PhysicalDrive%d", drive_id); + if ((err = ntfs_device_win32_simple_open_file(filename, &handle, flags, + TRUE))) { + /* open error */ + return err; + } + /* store the drive geometry */ + ntfs_device_win32_getgeo(handle, fd); + /* Just to be sure */ + if (fd->geo_size == -1) + fd->geo_size = ntfs_device_win32_getdisklength(handle); + /* fill fd */ + fd->ntdll = FALSE; + fd->handle = handle; + fd->part_start = 0; + fd->part_length = fd->geo_size; + fd->pos = 0; + fd->part_hidden_sectors = -1; + fd->vol_handle = INVALID_HANDLE_VALUE; + return 0; +} + +/** + * ntfs_device_win32_open_lowlevel - open a drive via low level win32 API + * @drive_id: drive to open + * @fd: pointer to win32 file device in which to put the result + * @flags: unix open status flags + * + * return 0 if o.k. + * -1 if not, and errno set. + */ +static __inline__ int ntfs_device_win32_open_lowlevel(int drive_id, + win32_fd *fd, int flags) +{ + HANDLE handle; + NTSTATUS st; + ACCESS_MASK access; + ULONG share; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io_status; + UNICODE_STRING unicode_name; + ntfschar unicode_buffer[7]; + int mode; + static const ntfschar unicode_init[] = { + const_cpu_to_le16('\\'), const_cpu_to_le16('?'), + const_cpu_to_le16('?'), const_cpu_to_le16('\\'), + const_cpu_to_le16(' '), const_cpu_to_le16(':'), + const_cpu_to_le16(0) + }; + + memcpy(unicode_buffer, unicode_init, sizeof(unicode_buffer)); + unicode_buffer[4] = cpu_to_le16(drive_id + 'A'); + unicode_name.Buffer = unicode_buffer; + unicode_name.Length = 6*sizeof(ntfschar); + unicode_name.MaximumLength = 6*sizeof(ntfschar); + + attr.Length = sizeof(OBJECT_ATTRIBUTES); + attr.RootDirectory = (HANDLE*)NULL; + attr.ObjectName = &unicode_name; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.SecurityDescriptor = (void*)NULL; + attr.SecurityQualityOfService = (void*)NULL; + + io_status.Status = 0; + io_status.Information = 0; + mode = flags & O_ACCMODE; + share = (mode == O_RDWR ? + 0 : FILE_SHARE_READ | FILE_SHARE_WRITE); + access = (mode == O_RDWR ? + FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE + : FILE_READ_DATA | SYNCHRONIZE); + + st = NtOpenFile(&handle, access, + &attr, &io_status, + share, + FILE_SYNCHRONOUS_IO_ALERT); + if (st != STATUS_SUCCESS) { + errno = ntfs_ntstatus_to_errno(st); + return (-1); + } + ntfs_device_win32_setlock(handle,FSCTL_LOCK_VOLUME); + /* store the drive geometry */ + ntfs_device_win32_getntgeo(handle, fd); + fd->ntdll = TRUE; + /* allow accessing the full partition */ + st = NtFsControlFile(handle, (HANDLE)NULL, + (PIO_APC_ROUTINE)NULL, + (PVOID)NULL, &io_status, + FSCTL_ALLOW_EXTENDED_DASD_IO, + NULL, 0, NULL, 0); + if (st != STATUS_SUCCESS) { + errno = ntfs_ntstatus_to_errno(st); + NtClose(handle); + return (-1); + } + /* fill fd */ + fd->handle = handle; + fd->part_start = 0; + fd->part_length = fd->geo_size; + fd->pos = 0; + fd->part_hidden_sectors = -1; + fd->vol_handle = INVALID_HANDLE_VALUE; + return 0; +} + +/** + * ntfs_device_win32_open_volume_for_partition - find and open a volume + * + * Windows NT/2k/XP handles volumes instead of partitions. + * This function gets the partition details and return an open volume handle. + * That volume is the one whose only physical location on disk is the described + * partition. + * + * The function required Windows 2k/XP, otherwise it fails (gracefully). + * + * Return success: a valid open volume handle. + * fail : INVALID_HANDLE_VALUE + */ +static HANDLE ntfs_device_win32_open_volume_for_partition(unsigned int drive_id, + s64 part_offset, s64 part_length, int flags) +{ + HANDLE vol_find_handle; + TCHAR vol_name[MAX_PATH]; + + /* Make sure all the required imports exist. */ + if (!fnFindFirstVolume || !fnFindNextVolume || !fnFindVolumeClose) { + ntfs_log_trace("Required dll imports not found.\n"); + return INVALID_HANDLE_VALUE; + } + /* Start iterating through volumes. */ + ntfs_log_trace("Entering with drive_id=%d, part_offset=%lld, " + "path_length=%lld, flags=%d.\n", drive_id, + (unsigned long long)part_offset, + (unsigned long long)part_length, flags); + vol_find_handle = fnFindFirstVolume(vol_name, MAX_PATH); + /* If a valid handle could not be aquired, reply with "don't know". */ + if (vol_find_handle == INVALID_HANDLE_VALUE) { + ntfs_log_trace("FindFirstVolume failed.\n"); + return INVALID_HANDLE_VALUE; + } + do { + int vol_name_length; + HANDLE handle; + + /* remove trailing '/' from vol_name */ +#ifdef UNICODE + vol_name_length = wcslen(vol_name); +#else + vol_name_length = strlen(vol_name); +#endif + if (vol_name_length>0) + vol_name[vol_name_length-1]=0; + + ntfs_log_debug("Processing %s.\n", vol_name); + /* open the file */ + handle = CreateFile(vol_name, + ntfs_device_unix_status_flags_to_win32(flags), + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL); + if (handle != INVALID_HANDLE_VALUE) { + DWORD bytesReturned; +#define EXTENTS_SIZE sizeof(VOLUME_DISK_EXTENTS) + 9 * sizeof(DISK_EXTENT) + char extents[EXTENTS_SIZE]; + + /* Check physical locations. */ + if (DeviceIoControl(handle, + IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, + NULL, 0, extents, EXTENTS_SIZE, + &bytesReturned, NULL)) { + if (((VOLUME_DISK_EXTENTS *)extents)-> + NumberOfDiskExtents == 1) { + DISK_EXTENT *extent = &(( + VOLUME_DISK_EXTENTS *) + extents)->Extents[0]; + if ((extent->DiskNumber==drive_id) && + (extent->StartingOffset. + QuadPart==part_offset) + && (extent-> + ExtentLength.QuadPart + == part_length)) { + /* + * Eureka! (Archimedes, 287 BC, + * "I have found it!") + */ + fnFindVolumeClose( + vol_find_handle); + return handle; + } + } + } + } else + ntfs_log_trace("getExtents() Failed.\n"); + } while (fnFindNextVolume(vol_find_handle, vol_name, MAX_PATH)); + /* End of iteration through volumes. */ + ntfs_log_trace("Closing, volume was not found.\n"); + fnFindVolumeClose(vol_find_handle); + return INVALID_HANDLE_VALUE; +} + +/** + * ntfs_device_win32_find_partition - locates partition details by id. + * @handle: HANDLE to the PhysicalDrive + * @partition_id: the partition number to locate + * @part_offset: pointer to where to put the offset to the partition + * @part_length: pointer to where to put the length of the partition + * @hidden_sectors: pointer to where to put the hidden sectors + * + * This function requires an open PhysicalDrive handle and a partition_id. + * If a partition with the required id is found on the supplied device, + * the partition attributes are returned back. + * + * Returns: TRUE if found, and sets the output parameters. + * FALSE if not and errno is set to the error code. + */ +static BOOL ntfs_device_win32_find_partition(HANDLE handle, DWORD partition_id, + s64 *part_offset, s64 *part_length, int *hidden_sectors) +{ + DRIVE_LAYOUT_INFORMATION *drive_layout; + unsigned int err, buf_size, part_count; + DWORD i; + + /* + * There is no way to know the required buffer, so if the ioctl fails, + * try doubling the buffer size each time until the ioctl succeeds. + */ + part_count = 8; + do { + buf_size = sizeof(DRIVE_LAYOUT_INFORMATION) + + part_count * sizeof(PARTITION_INFORMATION); + drive_layout = (DRIVE_LAYOUT_INFORMATION*)ntfs_malloc(buf_size); + if (!drive_layout) { + errno = ENOMEM; + return FALSE; + } + if (DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, + 0, (BYTE*)drive_layout, buf_size, &i, NULL)) + break; + err = GetLastError(); + free(drive_layout); + if (err != ERROR_INSUFFICIENT_BUFFER) { + ntfs_log_trace("GetDriveLayout failed.\n"); + errno = ntfs_w32error_to_errno(err); + return FALSE; + } + ntfs_log_debug("More than %u partitions.\n", part_count); + part_count <<= 1; + if (part_count > 512) { + ntfs_log_trace("GetDriveLayout failed: More than 512 " + "partitions?\n"); + errno = ENOBUFS; + return FALSE; + } + } while (1); + for (i = 0; i < drive_layout->PartitionCount; i++) { + if (drive_layout->PartitionEntry[i].PartitionNumber == + partition_id) { + *part_offset = drive_layout->PartitionEntry[i]. + StartingOffset.QuadPart; + *part_length = drive_layout->PartitionEntry[i]. + PartitionLength.QuadPart; + *hidden_sectors = drive_layout->PartitionEntry[i]. + HiddenSectors; + free(drive_layout); + return TRUE; + } + } + free(drive_layout); + errno = ENOENT; + return FALSE; +} + +/** + * ntfs_device_win32_open_partition - open a partition via win32 API + * @drive_id: drive to open + * @partition_id: partition to open + * @fd: win32 file device to return + * @flags: unix open status flags + * + * Return 0 if o.k. + * -1 if not, and errno set. + * + * When fails, fd contents may have not been preserved. + */ +static int ntfs_device_win32_open_partition(int drive_id, + unsigned int partition_id, win32_fd *fd, int flags) +{ + s64 part_start, part_length; + HANDLE handle; + int err, hidden_sectors; + char drive_name[MAX_PATH]; + + sprintf(drive_name, "\\\\.\\PhysicalDrive%d", drive_id); + /* Open the entire device without locking, ask questions later */ + if ((err = ntfs_device_win32_simple_open_file(drive_name, &handle, + flags, FALSE))) { + /* error */ + return err; + } + if (ntfs_device_win32_find_partition(handle, partition_id, &part_start, + &part_length, &hidden_sectors)) { + s64 tmp; + HANDLE vol_handle = ntfs_device_win32_open_volume_for_partition( + drive_id, part_start, part_length, flags); + /* Store the drive geometry. */ + ntfs_device_win32_getgeo(handle, fd); + fd->handle = handle; + fd->pos = 0; + fd->part_start = part_start; + fd->part_length = part_length; + fd->part_hidden_sectors = hidden_sectors; + fd->geo_sector_size = 512; + fd->ntdll = FALSE; + tmp = ntfs_device_win32_getntfssize(vol_handle); + if (tmp > 0) + fd->geo_size = tmp; + else + fd->geo_size = fd->part_length; + if (vol_handle != INVALID_HANDLE_VALUE) { + if (((flags & O_RDWR) == O_RDWR) && + ntfs_device_win32_lock(vol_handle)) { + CloseHandle(vol_handle); + CloseHandle(handle); + return -1; + } + fd->vol_handle = vol_handle; + } else { + if ((flags & O_RDWR) == O_RDWR) { + /* Access if read-write, no volume found. */ + ntfs_log_trace("Partitions containing Spanned/" + "Mirrored volumes are not " + "supported in R/W status " + "yet.\n"); + CloseHandle(handle); + errno = EOPNOTSUPP; + return -1; + } + fd->vol_handle = INVALID_HANDLE_VALUE; + } + return 0; + } else { + ntfs_log_debug("Partition %u not found on drive %d.\n", + partition_id, drive_id); + CloseHandle(handle); + errno = ENODEV; + return -1; + } +} + +/** + * ntfs_device_win32_open - open a device + * @dev: a pointer to the NTFS_DEVICE to open + * @flags: unix open status flags + * + * @dev->d_name must hold the device name, the rest is ignored. + * Supported flags are O_RDONLY, O_WRONLY and O_RDWR. + * + * If name is in format "(hd[0-9],[0-9])" then open a partition. + * If name is in format "(hd[0-9])" then open a volume. + * Otherwise open a file. + */ +static int ntfs_device_win32_open(struct ntfs_device *dev, int flags) +{ + int drive_id = 0, numparams; + unsigned int part = 0; + char drive_char; + win32_fd fd; + int err; + + if (NDevOpen(dev)) { + errno = EBUSY; + return -1; + } + ntfs_device_win32_init_imports(); + numparams = sscanf(dev->d_name, "/dev/hd%c%u", &drive_char, &part); + if (!numparams + && (dev->d_name[1] == ':') + && (dev->d_name[2] == '\0')) { + drive_char = dev->d_name[0]; + numparams = 3; + drive_id = toupper(drive_char) - 'A'; + } + switch (numparams) { + case 0: + ntfs_log_debug("win32_open(%s) -> file.\n", dev->d_name); + err = ntfs_device_win32_open_file(dev->d_name, &fd, flags); + break; + case 1: + ntfs_log_debug("win32_open(%s) -> drive %d.\n", dev->d_name, + drive_id); + err = ntfs_device_win32_open_drive(drive_id, &fd, flags); + break; + case 2: + ntfs_log_debug("win32_open(%s) -> drive %d, part %u.\n", + dev->d_name, drive_id, part); + err = ntfs_device_win32_open_partition(drive_id, part, &fd, + flags); + break; + case 3: + ntfs_log_debug("win32_open(%s) -> drive %c:\n", + dev->d_name, drive_char); + err = ntfs_device_win32_open_lowlevel(drive_id, &fd, + flags); + break; + default: + ntfs_log_debug("win32_open(%s) -> unknwon file format.\n", + dev->d_name); + err = -1; + } + if (err) + return err; + ntfs_log_debug("win32_open(%s) -> %p, offset 0x%llx.\n", dev->d_name, + dev, fd.part_start); + /* Setup our read-only flag. */ + if ((flags & O_RDWR) != O_RDWR) + NDevSetReadOnly(dev); + dev->d_private = (win32_fd*)ntfs_malloc(sizeof(win32_fd)); + memcpy(dev->d_private, &fd, sizeof(win32_fd)); + NDevSetOpen(dev); + NDevClearDirty(dev); + return 0; +} + +/** + * ntfs_device_win32_seek - change current logical file position + * @dev: ntfs device obtained via ->open + * @offset: required offset from the whence anchor + * @whence: whence anchor specifying what @offset is relative to + * + * Return the new position on the volume on success and -1 on error with errno + * set to the error code. + * + * @whence may be one of the following: + * SEEK_SET - Offset is relative to file start. + * SEEK_CUR - Offset is relative to current position. + * SEEK_END - Offset is relative to end of file. + */ +static s64 ntfs_device_win32_seek(struct ntfs_device *dev, s64 offset, + int whence) +{ + s64 abs_ofs; + win32_fd *fd = (win32_fd *)dev->d_private; + + ntfs_log_trace("seek offset = 0x%llx, whence = %d.\n", offset, whence); + switch (whence) { + case SEEK_SET: + abs_ofs = offset; + break; + case SEEK_CUR: + abs_ofs = fd->pos + offset; + break; + case SEEK_END: + /* End of partition != end of disk. */ + if (fd->part_length == -1) { + ntfs_log_trace("Position relative to end of disk not " + "implemented.\n"); + errno = EOPNOTSUPP; + return -1; + } + abs_ofs = fd->part_length + offset; + break; + default: + ntfs_log_trace("Wrong mode %d.\n", whence); + errno = EINVAL; + return -1; + } + if ((abs_ofs < 0) + || (fd->ntdll && (abs_ofs > fd->part_length))) { + ntfs_log_trace("Seeking outsize seekable area.\n"); + errno = EINVAL; + return -1; + } + fd->pos = abs_ofs; + return abs_ofs; +} + +/** + * ntfs_device_win32_pio - positioned low level i/o + * @fd: win32 device descriptor obtained via ->open + * @pos: at which position to do i/o from/to + * @count: how many bytes should be transfered + * @b: source/destination buffer + * @write: TRUE if write transfer and FALSE if read transfer + * + * On success returns the number of bytes transfered (can be < @count) and on + * error returns -1 and errno set. Transfer starts from position @pos on @fd. + * + * Notes: + * - @pos, @buf, and @count must be aligned to geo_sector_size + * - When dealing with volumes, a single call must not span both volume + * and disk extents. + * - Does not use/set @fd->pos. + */ +static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 pos, + const s64 count, void *rbuf, const void *wbuf) +{ + LARGE_INTEGER li; + HANDLE handle; + DWORD bt; + BOOL res; + s64 bytes; + + ntfs_log_trace("pos = 0x%llx, count = 0x%llx, direction = %s.\n", + (long long)pos, (long long)count, write ? "write" : + "read"); + li.QuadPart = pos; + if (fd->vol_handle != INVALID_HANDLE_VALUE && pos < fd->geo_size) { + ntfs_log_debug("Transfering via vol_handle.\n"); + handle = fd->vol_handle; + } else { + ntfs_log_debug("Transfering via handle.\n"); + handle = fd->handle; + li.QuadPart += fd->part_start; + } + + if (fd->ntdll) { + IO_STATUS_BLOCK io_status; + NTSTATUS res; + LARGE_INTEGER offset; + + io_status.Status = STATUS_SUCCESS; + io_status.Information = 0; + offset.QuadPart = pos; + if (wbuf) { + res = NtWriteFile(fd->handle,(HANDLE)NULL, + (PIO_APC_ROUTINE)NULL,(void*)NULL, + &io_status, wbuf, count, + &offset, (PULONG)NULL); + } else { + res = NtReadFile(fd->handle,(HANDLE)NULL, + (PIO_APC_ROUTINE)NULL,(void*)NULL, + &io_status, rbuf, count, + &offset, (PULONG)NULL); + } + if (res == STATUS_SUCCESS) { + bytes = io_status.Information; + } else { + bytes = -1; + errno = ntfs_ntstatus_to_errno(res); + } + } else { + if (!fnSetFilePointerEx(handle, li, NULL, FILE_BEGIN)) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("SetFilePointer failed.\n"); + return -1; + } + if (wbuf) + res = WriteFile(handle, wbuf, count, &bt, NULL); + else + res = ReadFile(handle, rbuf, count, &bt, NULL); + bytes = bt; + if (!res) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("%sFile() failed.\n", write ? + "Write" : "Read"); + return -1; + } + if (rbuf && !pos) { + /* get the sector size from the boot sector */ + char *boot = (char*)rbuf; + fd->geo_sector_size = (boot[11] & 255) + + ((boot[12] & 255) << 8); + } + } + return bytes; +} + +/** + * ntfs_device_win32_pread_simple - positioned simple read + * @fd: win32 device descriptor obtained via ->open + * @pos: at which position to read from + * @count: how many bytes should be read + * @b: a pointer to where to put the contents + * + * On success returns the number of bytes read (can be < @count) and on error + * returns -1 and errno set. Read starts from position @pos. + * + * Notes: + * - @pos, @buf, and @count must be aligned to geo_sector_size. + * - When dealing with volumes, a single call must not span both volume + * and disk extents. + * - Does not use/set @fd->pos. + */ +static inline s64 ntfs_device_win32_pread_simple(win32_fd *fd, const s64 pos, + const s64 count, void *b) +{ + return ntfs_device_win32_pio(fd, pos, count, b, (void*)NULL); +} + +/** + * ntfs_device_win32_read - read bytes from an ntfs device + * @dev: ntfs device obtained via ->open + * @b: pointer to where to put the contents + * @count: how many bytes should be read + * + * On success returns the number of bytes actually read (can be < @count). + * On error returns -1 with errno set. + */ +static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count) +{ + s64 old_pos, to_read, i, br = 0; + win32_fd *fd = (win32_fd *)dev->d_private; + BYTE *alignedbuffer; + int old_ofs, ofs; + + old_pos = fd->pos; + old_ofs = ofs = old_pos & (fd->geo_sector_size - 1); + to_read = (ofs + count + fd->geo_sector_size - 1) & + ~(s64)(fd->geo_sector_size - 1); + /* Impose maximum of 2GB to be on the safe side. */ + if (to_read > 0x80000000) { + int delta = to_read - count; + to_read = 0x80000000; + count = to_read - delta; + } + ntfs_log_trace("fd = %p, b = %p, count = 0x%llx, pos = 0x%llx, " + "ofs = %i, to_read = 0x%llx.\n", fd, b, + (long long)count, (long long)old_pos, ofs, + (long long)to_read); + if (!((unsigned long)b & (fd->geo_sector_size - 1)) && !old_ofs && + !(count & (fd->geo_sector_size - 1))) + alignedbuffer = b; + else { + alignedbuffer = (BYTE *)VirtualAlloc(NULL, to_read, MEM_COMMIT, + PAGE_READWRITE); + if (!alignedbuffer) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("VirtualAlloc failed for read.\n"); + return -1; + } + } + if (fd->vol_handle != INVALID_HANDLE_VALUE && old_pos < fd->geo_size) { + s64 vol_to_read = fd->geo_size - old_pos; + if (count > vol_to_read) { + br = ntfs_device_win32_pread_simple(fd, + old_pos & ~(s64)(fd->geo_sector_size - 1), + ofs + vol_to_read, alignedbuffer); + if (br == -1) + goto read_error; + to_read -= br; + if (br < ofs) { + br = 0; + goto read_partial; + } + br -= ofs; + fd->pos += br; + ofs = fd->pos & (fd->geo_sector_size - 1); + if (br != vol_to_read) + goto read_partial; + } + } + i = ntfs_device_win32_pread_simple(fd, + fd->pos & ~(s64)(fd->geo_sector_size - 1), to_read, + alignedbuffer + br); + if (i == -1) { + if (br) + goto read_partial; + goto read_error; + } + if (i < ofs) + goto read_partial; + i -= ofs; + br += i; + if (br > count) + br = count; + fd->pos = old_pos + br; +read_partial: + if (alignedbuffer != b) { + memcpy((void*)b, alignedbuffer + old_ofs, br); + VirtualFree(alignedbuffer, 0, MEM_RELEASE); + } + return br; +read_error: + if (alignedbuffer != b) + VirtualFree(alignedbuffer, 0, MEM_RELEASE); + return -1; +} + +/** + * ntfs_device_win32_close - close an open ntfs deivce + * @dev: ntfs device obtained via ->open + * + * Return 0 if o.k. + * -1 if not, and errno set. Note if error fd->vol_handle is trashed. + */ +static int ntfs_device_win32_close(struct ntfs_device *dev) +{ + win32_fd *fd = (win32_fd *)dev->d_private; + BOOL rvl; + + ntfs_log_trace("Closing device %p.\n", dev); + if (!NDevOpen(dev)) { + errno = EBADF; + return -1; + } + if (fd->vol_handle != INVALID_HANDLE_VALUE) { + if (!NDevReadOnly(dev)) { + ntfs_device_win32_dismount(fd->vol_handle); + ntfs_device_win32_unlock(fd->vol_handle); + } + if (!CloseHandle(fd->vol_handle)) + ntfs_log_trace("CloseHandle() failed for volume.\n"); + } + if (fd->ntdll) { + ntfs_device_win32_setlock(fd->handle,FSCTL_UNLOCK_VOLUME); + rvl = NtClose(fd->handle) == STATUS_SUCCESS; + } else + rvl = CloseHandle(fd->handle); + NDevClearOpen(dev); + free(fd); + if (!rvl) { + errno = ntfs_w32error_to_errno(GetLastError()); + if (fd->ntdll) + ntfs_log_trace("NtClose() failed.\n"); + else + ntfs_log_trace("CloseHandle() failed.\n"); + return -1; + } + return 0; +} + +/** + * ntfs_device_win32_sync - flush write buffers to disk + * @dev: ntfs device obtained via ->open + * + * Return 0 if o.k. + * -1 if not, and errno set. + * + * Note: Volume syncing works differently in windows. + * Disk cannot be synced in windows. + */ +static int ntfs_device_win32_sync(struct ntfs_device *dev) +{ + int err = 0; + BOOL to_clear = TRUE; + + if (!NDevReadOnly(dev) && NDevDirty(dev)) { + win32_fd *fd = (win32_fd *)dev->d_private; + + if ((fd->vol_handle != INVALID_HANDLE_VALUE) && + !FlushFileBuffers(fd->vol_handle)) { + to_clear = FALSE; + err = ntfs_w32error_to_errno(GetLastError()); + } + if (!FlushFileBuffers(fd->handle)) { + to_clear = FALSE; + if (!err) + err = ntfs_w32error_to_errno(GetLastError()); + } + if (!to_clear) { + ntfs_log_trace("Could not sync.\n"); + errno = err; + return -1; + } + NDevClearDirty(dev); + } + return 0; +} + +/** + * ntfs_device_win32_pwrite_simple - positioned simple write + * @fd: win32 device descriptor obtained via ->open + * @pos: at which position to write to + * @count: how many bytes should be written + * @b: a pointer to the data to write + * + * On success returns the number of bytes written and on error returns -1 and + * errno set. Write starts from position @pos. + * + * Notes: + * - @pos, @buf, and @count must be aligned to geo_sector_size. + * - When dealing with volumes, a single call must not span both volume + * and disk extents. + * - Does not use/set @fd->pos. + */ +static inline s64 ntfs_device_win32_pwrite_simple(win32_fd *fd, const s64 pos, + const s64 count, const void *b) +{ + return ntfs_device_win32_pio(fd, pos, count, (void*)NULL, b); +} + +/** + * ntfs_device_win32_write - write bytes to an ntfs device + * @dev: ntfs device obtained via ->open + * @b: pointer to the data to write + * @count: how many bytes should be written + * + * On success returns the number of bytes actually written. + * On error returns -1 with errno set. + */ +static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, + s64 count) +{ + s64 old_pos, to_write, i, bw = 0; + win32_fd *fd = (win32_fd *)dev->d_private; + const BYTE *alignedbuffer; + BYTE *readbuffer; + int old_ofs, ofs; + + old_pos = fd->pos; + old_ofs = ofs = old_pos & (fd->geo_sector_size - 1); + to_write = (ofs + count + fd->geo_sector_size - 1) & + ~(s64)(fd->geo_sector_size - 1); + /* Impose maximum of 2GB to be on the safe side. */ + if (to_write > 0x80000000) { + int delta = to_write - count; + to_write = 0x80000000; + count = to_write - delta; + } + ntfs_log_trace("fd = %p, b = %p, count = 0x%llx, pos = 0x%llx, " + "ofs = %i, to_write = 0x%llx.\n", fd, b, + (long long)count, (long long)old_pos, ofs, + (long long)to_write); + if (NDevReadOnly(dev)) { + ntfs_log_trace("Can't write on a R/O device.\n"); + errno = EROFS; + return -1; + } + if (!count) + return 0; + NDevSetDirty(dev); + readbuffer = (BYTE*)NULL; + if (!((unsigned long)b & (fd->geo_sector_size - 1)) && !old_ofs && + !(count & (fd->geo_sector_size - 1))) + alignedbuffer = (const BYTE *)b; + else { + s64 end; + + readbuffer = (BYTE *)VirtualAlloc(NULL, to_write, + MEM_COMMIT, PAGE_READWRITE); + if (!readbuffer) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("VirtualAlloc failed for write.\n"); + return -1; + } + /* Read first sector if start of write not sector aligned. */ + if (ofs) { + i = ntfs_device_win32_pread_simple(fd, + old_pos & ~(s64)(fd->geo_sector_size - 1), + fd->geo_sector_size, readbuffer); + if (i != fd->geo_sector_size) { + if (i >= 0) + errno = EIO; + goto write_error; + } + } + /* + * Read last sector if end of write not sector aligned and last + * sector is either not the same as the first sector or it is + * the same as the first sector but this has not been read in + * yet, i.e. the start of the write is sector aligned. + */ + end = old_pos + count; + if ((end & (fd->geo_sector_size - 1)) && + ((to_write > fd->geo_sector_size) || !ofs)) { + i = ntfs_device_win32_pread_simple(fd, + end & ~(s64)(fd->geo_sector_size - 1), + fd->geo_sector_size, readbuffer + + to_write - fd->geo_sector_size); + if (i != fd->geo_sector_size) { + if (i >= 0) + errno = EIO; + goto write_error; + } + } + /* Copy the data to be written into @readbuffer. */ + memcpy(readbuffer + ofs, b, count); + alignedbuffer = readbuffer; + } + if (fd->vol_handle != INVALID_HANDLE_VALUE && old_pos < fd->geo_size) { + s64 vol_to_write = fd->geo_size - old_pos; + if (count > vol_to_write) { + bw = ntfs_device_win32_pwrite_simple(fd, + old_pos & ~(s64)(fd->geo_sector_size - 1), + ofs + vol_to_write, alignedbuffer); + if (bw == -1) + goto write_error; + to_write -= bw; + if (bw < ofs) { + bw = 0; + goto write_partial; + } + bw -= ofs; + fd->pos += bw; + ofs = fd->pos & (fd->geo_sector_size - 1); + if (bw != vol_to_write) + goto write_partial; + } + } + i = ntfs_device_win32_pwrite_simple(fd, + fd->pos & ~(s64)(fd->geo_sector_size - 1), to_write, + alignedbuffer + bw); + if (i == -1) { + if (bw) + goto write_partial; + goto write_error; + } + if (i < ofs) + goto write_partial; + i -= ofs; + bw += i; + if (bw > count) + bw = count; + fd->pos = old_pos + bw; +write_partial: + if (readbuffer) + VirtualFree(readbuffer, 0, MEM_RELEASE); + return bw; +write_error: + bw = -1; + goto write_partial; +} + +/** + * ntfs_device_win32_stat - get a unix-like stat structure for an ntfs device + * @dev: ntfs device obtained via ->open + * @buf: pointer to the stat structure to fill + * + * Note: Only st_mode, st_size, and st_blocks are filled. + * + * Return 0 if o.k. + * -1 if not and errno set. in this case handle is trashed. + */ +static int ntfs_device_win32_stat(struct ntfs_device *dev, struct stat *buf) +{ + win32_fd *fd = (win32_fd *)dev->d_private; + mode_t st_mode; + + if ((dev->d_name[1] == ':') && (dev->d_name[2] == '\0')) + st_mode = S_IFBLK; + else + switch (GetFileType(fd->handle)) { + case FILE_TYPE_CHAR: + st_mode = S_IFCHR; + break; + case FILE_TYPE_DISK: + st_mode = S_IFREG; + break; + case FILE_TYPE_PIPE: + st_mode = S_IFIFO; + break; + default: + st_mode = 0; + } + memset(buf, 0, sizeof(struct stat)); + buf->st_mode = st_mode; + buf->st_size = fd->part_length; + if (buf->st_size != -1) + buf->st_blocks = buf->st_size >> 9; + else + buf->st_size = 0; + return 0; +} + +#ifdef HDIO_GETGEO +/** + * ntfs_win32_hdio_getgeo - get drive geometry + * @dev: ntfs device obtained via ->open + * @argp: pointer to where to put the output + * + * Note: Works on windows NT/2k/XP only. + * + * Return 0 if o.k. + * -1 if not, and errno set. Note if error fd->handle is trashed. + */ +static __inline__ int ntfs_win32_hdio_getgeo(struct ntfs_device *dev, + struct hd_geometry *argp) +{ + win32_fd *fd = (win32_fd *)dev->d_private; + + argp->heads = fd->geo_heads; + argp->sectors = fd->geo_sectors; + argp->cylinders = fd->geo_cylinders; + argp->start = fd->part_hidden_sectors; + return 0; +} +#endif + +/** + * ntfs_win32_blksszget - get block device sector size + * @dev: ntfs device obtained via ->open + * @argp: pointer to where to put the output + * + * Note: Works on windows NT/2k/XP only. + * + * Return 0 if o.k. + * -1 if not, and errno set. Note if error fd->handle is trashed. + */ +static __inline__ int ntfs_win32_blksszget(struct ntfs_device *dev,int *argp) +{ + win32_fd *fd = (win32_fd *)dev->d_private; + DWORD bytesReturned; + DISK_GEOMETRY dg; + + if (DeviceIoControl(fd->handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, + &dg, sizeof(DISK_GEOMETRY), &bytesReturned, NULL)) { + /* success */ + *argp = dg.BytesPerSector; + return 0; + } + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("GET_DRIVE_GEOMETRY failed.\n"); + return -1; +} + +static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request, + void *argp) +{ +#if defined(BLKGETSIZE) | defined(BLKGETSIZE64) + win32_fd *fd = (win32_fd *)dev->d_private; +#endif + + ntfs_log_trace("win32_ioctl(%d) called.\n", request); + switch (request) { +#if defined(BLKGETSIZE) + case BLKGETSIZE: + ntfs_log_debug("BLKGETSIZE detected.\n"); + if (fd->part_length >= 0) { + *(int *)argp = (int)(fd->part_length / 512); + return 0; + } + errno = EOPNOTSUPP; + return -1; +#endif +#if defined(BLKGETSIZE64) + case BLKGETSIZE64: + ntfs_log_debug("BLKGETSIZE64 detected.\n"); + if (fd->part_length >= 0) { + *(s64 *)argp = fd->part_length; + return 0; + } + errno = EOPNOTSUPP; + return -1; +#endif +#ifdef HDIO_GETGEO + case HDIO_GETGEO: + ntfs_log_debug("HDIO_GETGEO detected.\n"); + return ntfs_win32_hdio_getgeo(dev, (struct hd_geometry *)argp); +#endif +#ifdef BLKSSZGET + case BLKSSZGET: + ntfs_log_debug("BLKSSZGET detected.\n"); + return ntfs_win32_blksszget(dev, (int *)argp); +#endif +#ifdef BLKBSZSET + case BLKBSZSET: + ntfs_log_debug("BLKBSZSET detected.\n"); + /* Nothing to do on Windows. */ + return 0; +#endif + default: + ntfs_log_debug("unimplemented ioctl %d.\n", request); + errno = EOPNOTSUPP; + return -1; + } +} + +static s64 ntfs_device_win32_pread(struct ntfs_device *dev, void *b, + s64 count, s64 offset) +{ + s64 got; + win32_fd *fd; + + /* read the fast way if sector aligned */ + fd = (win32_fd*)dev->d_private; + if (!((count | offset) & (fd->geo_sector_size - 1))) { + got = ntfs_device_win32_pio(fd, offset, count, b, (void*)NULL); + } else { + if (ntfs_device_win32_seek(dev, offset, 0) == -1) + got = 0; + else + got = ntfs_device_win32_read(dev, b, count); + } + + return (got); +} + +static s64 ntfs_device_win32_pwrite(struct ntfs_device *dev, const void *b, + s64 count, s64 offset) +{ + s64 put; + win32_fd *fd; + + /* write the fast way if sector aligned */ + fd = (win32_fd*)dev->d_private; + if (!((count | offset) & (fd->geo_sector_size - 1))) { + put = ntfs_device_win32_pio(fd, offset, count, (void*)NULL, b); + } else { + if (ntfs_device_win32_seek(dev, offset, 0) == -1) + put = 0; + else + put = ntfs_device_win32_write(dev, b, count); + } + return (put); +} + +struct ntfs_device_operations ntfs_device_win32_io_ops = { + .open = ntfs_device_win32_open, + .close = ntfs_device_win32_close, + .seek = ntfs_device_win32_seek, + .read = ntfs_device_win32_read, + .write = ntfs_device_win32_write, + .pread = ntfs_device_win32_pread, + .pwrite = ntfs_device_win32_pwrite, + .sync = ntfs_device_win32_sync, + .stat = ntfs_device_win32_stat, + .ioctl = ntfs_device_win32_ioctl +}; + +/* + * Mark an open file as sparse + * + * This is only called by ntfsclone when cloning a volume to a file. + * The argument is the target file, not a volume. + * + * Returns 0 if successful. + */ + +int ntfs_win32_set_sparse(int fd) +{ + BOOL ok; + HANDLE handle; + DWORD bytes; + + handle = get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) + ok = FALSE; + else + ok = DeviceIoControl(handle, FSCTL_SET_SPARSE, + (void*)NULL, 0, (void*)NULL, 0, + &bytes, (LPOVERLAPPED)NULL); + return (!ok); +} + +/* + * Resize an open file + * + * This is only called by ntfsclone when cloning a volume to a file. + * The argument must designate a file, not a volume. + * + * Returns 0 if successful. + */ + +static int win32_ftruncate(HANDLE handle, s64 size) +{ + BOOL ok; + LONG hsize, lsize; + LONG ohsize, olsize; + + if (handle == INVALID_HANDLE_VALUE) + ok = FALSE; + else { + SetLastError(NO_ERROR); + /* save original position */ + ohsize = 0; + olsize = SetFilePointer(handle, 0, &ohsize, 1); + hsize = size >> 32; + lsize = size & 0xffffffff; + ok = (SetFilePointer(handle, lsize, &hsize, 0) == (DWORD)lsize) + && (GetLastError() == NO_ERROR) + && SetEndOfFile(handle); + /* restore original position, even if above failed */ + SetFilePointer(handle, olsize, &ohsize, 0); + if (GetLastError() != NO_ERROR) + ok = FALSE; + } + if (!ok) + errno = EINVAL; + return (ok ? 0 : -1); +} + +int ntfs_device_win32_ftruncate(struct ntfs_device *dev, s64 size) +{ + win32_fd *fd; + int ret; + + ret = -1; + fd = (win32_fd*)dev->d_private; + if (fd && !fd->ntdll) + ret = win32_ftruncate(fd->handle, size); + return (ret); +} + +int ntfs_win32_ftruncate(int fd, s64 size) +{ + int ret; + HANDLE handle; + + handle = get_osfhandle(fd); + ret = win32_ftruncate(handle, size); + return (ret); +} diff --git a/libntfs-3g/xattrs.c b/libntfs-3g/xattrs.c new file mode 100755 index 0000000000000000000000000000000000000000..6da814677a87fa203f8da7c2bf92ce14d73c3b20 --- /dev/null +++ b/libntfs-3g/xattrs.c @@ -0,0 +1,802 @@ +/** + * xattrs.c : common functions to deal with system extended attributes + * + * Copyright (c) 2010-2014 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SETXATTR /* extended attributes support required */ + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "types.h" +#include "param.h" +#include "layout.h" +#include "attrib.h" +#include "index.h" +#include "dir.h" +#include "security.h" +#include "acls.h" +#include "efs.h" +#include "reparse.h" +#include "object_id.h" +#include "ea.h" +#include "misc.h" +#include "logging.h" +#include "xattrs.h" + +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + +/* + * Posix ACL structures + */ + +struct LE_POSIX_ACE { + le16 tag; + le16 perms; + le32 id; +} __attribute__((__packed__)); + +struct LE_POSIX_ACL { + u8 version; + u8 flags; + le16 filler; + struct LE_POSIX_ACE ace[0]; +} __attribute__((__packed__)); + +#endif +#endif + +static const char xattr_ntfs_3g[] = "ntfs-3g."; +static const char nf_ns_user_prefix[] = "user."; +static const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1; + +static const char nf_ns_xattr_ntfs_acl[] = "system.ntfs_acl"; +static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib"; +static const char nf_ns_xattr_attrib_be[] = "system.ntfs_attrib_be"; +static const char nf_ns_xattr_efsinfo[] = "system.ntfs_efsinfo"; +static const char nf_ns_xattr_reparse[] = "system.ntfs_reparse_data"; +static const char nf_ns_xattr_object_id[] = "system.ntfs_object_id"; +static const char nf_ns_xattr_dos_name[] = "system.ntfs_dos_name"; +static const char nf_ns_xattr_times[] = "system.ntfs_times"; +static const char nf_ns_xattr_times_be[] = "system.ntfs_times_be"; +static const char nf_ns_xattr_crtime[] = "system.ntfs_crtime"; +static const char nf_ns_xattr_crtime_be[] = "system.ntfs_crtime_be"; +static const char nf_ns_xattr_ea[] = "system.ntfs_ea"; +static const char nf_ns_xattr_posix_access[] = "system.posix_acl_access"; +static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default"; + +static const char nf_ns_alt_xattr_efsinfo[] = "user.ntfs.efsinfo"; + +struct XATTRNAME { + enum SYSTEMXATTRS xattr; + const char *name; +} ; + +static struct XATTRNAME nf_ns_xattr_names[] = { + { XATTR_NTFS_ACL, nf_ns_xattr_ntfs_acl }, + { XATTR_NTFS_ATTRIB, nf_ns_xattr_attrib }, + { XATTR_NTFS_ATTRIB_BE, nf_ns_xattr_attrib_be }, + { XATTR_NTFS_EFSINFO, nf_ns_xattr_efsinfo }, + { XATTR_NTFS_REPARSE_DATA, nf_ns_xattr_reparse }, + { XATTR_NTFS_OBJECT_ID, nf_ns_xattr_object_id }, + { XATTR_NTFS_DOS_NAME, nf_ns_xattr_dos_name }, + { XATTR_NTFS_TIMES, nf_ns_xattr_times }, + { XATTR_NTFS_TIMES_BE, nf_ns_xattr_times_be }, + { XATTR_NTFS_CRTIME, nf_ns_xattr_crtime }, + { XATTR_NTFS_CRTIME_BE, nf_ns_xattr_crtime_be }, + { XATTR_NTFS_EA, nf_ns_xattr_ea }, + { XATTR_POSIX_ACC, nf_ns_xattr_posix_access }, + { XATTR_POSIX_DEF, nf_ns_xattr_posix_default }, + { XATTR_UNMAPPED, (char*)NULL } /* terminator */ +}; + +/* + * Make an integer big-endian + * + * Swap bytes on a small-endian computer and does nothing on a + * big-endian computer. + */ + +static void fix_big_endian(char *p, int size) +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + int i,j; + int c; + + i = 0; + j = size - 1; + while (i < j) { + c = p[i]; + p[i++] = p[j]; + p[j--] = c; + } +#endif +} + +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + +/* + * Make a Posix ACL CPU endian + */ + +static int le_acl_to_cpu(const struct LE_POSIX_ACL *le_acl, size_t size, + struct POSIX_ACL *acl) +{ + int i; + int cnt; + + acl->version = le_acl->version; + acl->flags = le_acl->flags; + acl->filler = 0; + cnt = (size - sizeof(struct LE_POSIX_ACL)) / sizeof(struct LE_POSIX_ACE); + for (i=0; i<cnt; i++) { + acl->ace[i].tag = le16_to_cpu(le_acl->ace[i].tag); + acl->ace[i].perms = le16_to_cpu(le_acl->ace[i].perms); + acl->ace[i].id = le32_to_cpu(le_acl->ace[i].id); + } + return (0); +} + +/* + * Make a Posix ACL little endian + */ + +int cpu_to_le_acl(const struct POSIX_ACL *acl, size_t size, + struct LE_POSIX_ACL *le_acl) +{ + int i; + int cnt; + + le_acl->version = acl->version; + le_acl->flags = acl->flags; + le_acl->filler = const_cpu_to_le16(0); + cnt = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE); + for (i=0; i<cnt; i++) { + le_acl->ace[i].tag = cpu_to_le16(acl->ace[i].tag); + le_acl->ace[i].perms = cpu_to_le16(acl->ace[i].perms); + le_acl->ace[i].id = cpu_to_le32(acl->ace[i].id); + } + return (0); +} + +#endif +#endif + +/* + * Determine whether an extended attribute is mapped to + * internal data (original name in system namespace, or renamed) + */ + +enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name, + ntfs_volume *vol) +{ + struct XATTRNAME *p; + enum SYSTEMXATTRS ret; +#ifdef XATTR_MAPPINGS + const struct XATTRMAPPING *q; +#endif /* XATTR_MAPPINGS */ + + p = nf_ns_xattr_names; + while (p->name && strcmp(p->name,name)) + p++; + ret = p->xattr; +#ifdef XATTR_MAPPINGS + if (!p->name && vol && vol->xattr_mapping) { + q = vol->xattr_mapping; + while (q && strcmp(q->name,name)) + q = q->next; + if (q) + ret = q->xattr; + } +#else /* XATTR_MAPPINGS */ + if (!p->name + && vol + && vol->efs_raw + && !strcmp(nf_ns_alt_xattr_efsinfo,name)) + ret = XATTR_NTFS_EFSINFO; +#endif /* XATTR_MAPPINGS */ + return (ret); +} + +#ifdef XATTR_MAPPINGS + +/* + * Basic read from a user mapping file on another volume + */ + +static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused))) +{ + return (read(*(int*)fileid, buf, size)); +} + + +/* + * Read from a user mapping file on current NTFS partition + */ + +static int localread(void *fileid, char *buf, size_t size, off_t offs) +{ + return (ntfs_attr_data_read((ntfs_inode*)fileid, + AT_UNNAMED, 0, buf, size, offs)); +} + +/* + * Get a single mapping item from buffer + * + * Always reads a full line, truncating long lines + * Refills buffer when exhausted + * Returns pointer to item, or NULL when there is no more + * Note : errors are logged, but not returned +// TODO partially share with acls.c + */ + +static struct XATTRMAPPING *getmappingitem(FILEREADER reader, void *fileid, + off_t *poffs, char *buf, int *psrc, s64 *psize) +{ + int src; + int dst; + char *pe; + char *ps; + char *pu; + enum SYSTEMXATTRS xattr; + int gotend; + char maptext[LINESZ]; + struct XATTRMAPPING *item; + + src = *psrc; + dst = 0; + do { + gotend = 0; + while ((src < *psize) + && (buf[src] != '\n')) { + /* ignore spaces */ + if ((dst < LINESZ) + && (buf[src] != '\r') + && (buf[src] != '\t') + && (buf[src] != ' ')) + maptext[dst++] = buf[src]; + src++; + } + if (src >= *psize) { + *poffs += *psize; + *psize = reader(fileid, buf, (size_t)BUFSZ, *poffs); + src = 0; + } else { + gotend = 1; + src++; + maptext[dst] = '\0'; + dst = 0; + } + } while (*psize && ((maptext[0] == '#') || !gotend)); + item = (struct XATTRMAPPING*)NULL; + if (gotend) { + /* decompose into system name and user name */ + ps = maptext; + pu = strchr(maptext,':'); + if (pu) { + *pu++ = 0; + pe = strchr(pu,':'); + if (pe) + *pe = 0; + /* check name validity */ + if ((strlen(pu) < 6) || strncmp(pu,"user.",5)) + pu = (char*)NULL; + xattr = ntfs_xattr_system_type(ps, + (ntfs_volume*)NULL); + if (xattr == XATTR_UNMAPPED) + pu = (char*)NULL; + } + if (pu) { + item = (struct XATTRMAPPING*)ntfs_malloc( + sizeof(struct XATTRMAPPING) + + strlen(pu)); + if (item) { + item->xattr = xattr; + strcpy(item->name,pu); + item->next = (struct XATTRMAPPING*)NULL; + } + } else { + ntfs_log_early_error("Bad xattr mapping item, aborting\n"); + } + } + *psrc = src; + return (item); +} + +/* + * Read xattr mapping file and split into their attribute. + * Parameters are kept in a chained list. + * Returns the head of list, if any + * Errors are logged, but not returned + * + * If an absolute path is provided, the mapping file is assumed + * to be located in another mounted file system, and plain read() + * are used to get its contents. + * If a relative path is provided, the mapping file is assumed + * to be located on the current file system, and internal IO + * have to be used since we are still mounting and we have not + * entered the fuse loop yet. + */ + +static struct XATTRMAPPING *ntfs_read_xattr_mapping(FILEREADER reader, + void *fileid) +{ + char buf[BUFSZ]; + struct XATTRMAPPING *item; + struct XATTRMAPPING *current; + struct XATTRMAPPING *firstitem; + struct XATTRMAPPING *lastitem; + BOOL duplicated; + int src; + off_t offs; + s64 size; + + firstitem = (struct XATTRMAPPING*)NULL; + lastitem = (struct XATTRMAPPING*)NULL; + offs = 0; + size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0); + if (size > 0) { + src = 0; + do { + item = getmappingitem(reader, fileid, &offs, + buf, &src, &size); + if (item) { + /* check no double mapping */ + duplicated = FALSE; + for (current=firstitem; current; current=current->next) + if ((current->xattr == item->xattr) + || !strcmp(current->name,item->name)) + duplicated = TRUE; + if (duplicated) { + free(item); + ntfs_log_early_error("Conflicting xattr mapping ignored\n"); + } else { + item->next = (struct XATTRMAPPING*)NULL; + if (lastitem) + lastitem->next = item; + else + firstitem = item; + lastitem = item; + } + } + } while (item); + } + return (firstitem); +} + +/* + * Build the extended attribute mappings to user namespace + * + * Note : no error is returned. If we refused mounting when there + * is an error it would be too difficult to fix the offending file + */ + +struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol, + const char *xattrmap_path) +{ + struct XATTRMAPPING *firstmapping; + struct XATTRMAPPING *mapping; + BOOL user_efs; + BOOL notfound; + ntfs_inode *ni; + int fd; + + firstmapping = (struct XATTRMAPPING*)NULL; + notfound = FALSE; + if (!xattrmap_path) + xattrmap_path = XATTRMAPPINGFILE; + if (xattrmap_path[0] == '/') { + fd = open(xattrmap_path,O_RDONLY); + if (fd > 0) { + firstmapping = ntfs_read_xattr_mapping(basicread, (void*)&fd); + close(fd); + } else + notfound = TRUE; + } else { + ni = ntfs_pathname_to_inode(vol, NULL, xattrmap_path); + if (ni) { + firstmapping = ntfs_read_xattr_mapping(localread, ni); + ntfs_inode_close(ni); + } else + notfound = TRUE; + } + if (notfound && strcmp(xattrmap_path, XATTRMAPPINGFILE)) { + ntfs_log_early_error("Could not open \"%s\"\n",xattrmap_path); + } + if (vol->efs_raw) { + user_efs = TRUE; + for (mapping=firstmapping; mapping; mapping=mapping->next) + if (mapping->xattr == XATTR_NTFS_EFSINFO) + user_efs = FALSE; + } else + user_efs = FALSE; + if (user_efs) { + mapping = (struct XATTRMAPPING*)ntfs_malloc( + sizeof(struct XATTRMAPPING) + + strlen(nf_ns_alt_xattr_efsinfo)); + if (mapping) { + mapping->next = firstmapping; + mapping->xattr = XATTR_NTFS_EFSINFO; + strcpy(mapping->name,nf_ns_alt_xattr_efsinfo); + firstmapping = mapping; + } + } + return (firstmapping); +} + +void ntfs_xattr_free_mapping(struct XATTRMAPPING *mapping) +{ + struct XATTRMAPPING *p, *q; + + p = mapping; + while (p) { + q = p->next; + free(p); + p = q; + } +} + +#endif /* XATTR_MAPPINGS */ + +int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size) +{ + int res; + int i; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + struct POSIX_ACL *acl; +#endif +#endif + + /* + * the returned value is the needed + * size. If it is too small, no copy + * is done, and the caller has to + * issue a new call with correct size. + */ + switch (attr) { + case XATTR_NTFS_ACL : + res = ntfs_get_ntfs_acl(scx, ni, value, size); + break; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + case XATTR_POSIX_ACC : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + res = ntfs_get_posix_acl(scx, ni, + nf_ns_xattr_posix_access, (char*)acl, size); + if (res > 0) { + if (cpu_to_le_acl(acl,res, + (struct LE_POSIX_ACL*)value)) + res = -errno; + } + free(acl); + } else + res = -errno; + break; + case XATTR_POSIX_DEF : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + res = ntfs_get_posix_acl(scx, ni, + nf_ns_xattr_posix_default, (char*)acl, size); + if (res > 0) { + if (cpu_to_le_acl(acl,res, + (struct LE_POSIX_ACL*)value)) + res = -errno; + } + free(acl); + } else + res = -errno; + break; +#else + case XATTR_POSIX_ACC : + res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_access, + value, size); + break; + case XATTR_POSIX_DEF : + res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_default, + value, size); + break; +#endif +#endif + case XATTR_NTFS_ATTRIB : + res = ntfs_get_ntfs_attrib(ni, value, size); + break; + case XATTR_NTFS_ATTRIB_BE : + res = ntfs_get_ntfs_attrib(ni, value, size); + if ((res == 4) && value) { + if (size >= 4) + fix_big_endian(value,4); + else + res = -EINVAL; + } + break; + case XATTR_NTFS_EFSINFO : + if (ni->vol->efs_raw) + res = ntfs_get_efs_info(ni, value, size); + else + res = -EPERM; + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_get_ntfs_reparse_data(ni, value, size); + break; + case XATTR_NTFS_OBJECT_ID : + res = ntfs_get_ntfs_object_id(ni, value, size); + break; + case XATTR_NTFS_DOS_NAME: + if (dir_ni) + res = ntfs_get_ntfs_dos_name(ni, dir_ni, value, size); + else + res = -errno; + break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_get_times(ni, value, size); + break; + case XATTR_NTFS_TIMES_BE: + res = ntfs_inode_get_times(ni, value, size); + if ((res > 0) && value) { + for (i=0; (i+1)*sizeof(u64)<=(unsigned int)res; i++) + fix_big_endian(&value[i*sizeof(u64)], + sizeof(u64)); + } + break; + case XATTR_NTFS_CRTIME: + res = ntfs_inode_get_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size)); + break; + case XATTR_NTFS_CRTIME_BE: + res = ntfs_inode_get_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size)); + if ((res >= (int)sizeof(u64)) && value) + fix_big_endian(value,sizeof(u64)); + break; + case XATTR_NTFS_EA : + res = ntfs_get_ntfs_ea(ni, value, size); + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags) +{ + int res; + int i; + char buf[4*sizeof(u64)]; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + struct POSIX_ACL *acl; +#endif +#endif + + switch (attr) { + case XATTR_NTFS_ACL : + res = ntfs_set_ntfs_acl(scx, ni, value, size, flags); + break; +#if POSIXACLS +#if __BYTE_ORDER == __BIG_ENDIAN + case XATTR_POSIX_ACC : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + if (!le_acl_to_cpu((const struct LE_POSIX_ACL*)value, + size, acl)) { + res = ntfs_set_posix_acl(scx ,ni , + nf_ns_xattr_posix_access, + (char*)acl, size, flags); + } else + res = -errno; + free(acl); + } else + res = -errno; + break; + case XATTR_POSIX_DEF : + acl = (struct POSIX_ACL*)ntfs_malloc(size); + if (acl) { + if (!le_acl_to_cpu((const struct LE_POSIX_ACL*)value, + size, acl)) { + res = ntfs_set_posix_acl(scx ,ni , + nf_ns_xattr_posix_default, + (char*)acl, size, flags); + } else + res = -errno; + free(acl); + } else + res = -errno; + break; +#else + case XATTR_POSIX_ACC : + res = ntfs_set_posix_acl(scx ,ni , nf_ns_xattr_posix_access, + value, size, flags); + break; + case XATTR_POSIX_DEF : + res = ntfs_set_posix_acl(scx, ni, nf_ns_xattr_posix_default, + value, size, flags); + break; +#endif +#endif + case XATTR_NTFS_ATTRIB : + res = ntfs_set_ntfs_attrib(ni, value, size, flags); + break; + case XATTR_NTFS_ATTRIB_BE : + if (value && (size >= 4)) { + memcpy(buf,value,4); + fix_big_endian(buf,4); + res = ntfs_set_ntfs_attrib(ni, buf, 4, flags); + } else + res = ntfs_set_ntfs_attrib(ni, value, size, flags); + break; + case XATTR_NTFS_EFSINFO : + if (ni->vol->efs_raw) + res = ntfs_set_efs_info(ni, value, size, flags); + else + res = -EPERM; + break; + case XATTR_NTFS_REPARSE_DATA : + res = ntfs_set_ntfs_reparse_data(ni, value, size, flags); + break; + case XATTR_NTFS_OBJECT_ID : + res = ntfs_set_ntfs_object_id(ni, value, size, flags); + break; + case XATTR_NTFS_DOS_NAME: + if (dir_ni) + /* warning : this closes both inodes */ + res = ntfs_set_ntfs_dos_name(ni, dir_ni, value, + size, flags); + else + res = -errno; + break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_set_times(ni, value, size, flags); + break; + case XATTR_NTFS_TIMES_BE: + if (value && (size > 0) && (size <= 4*sizeof(u64))) { + memcpy(buf,value,size); + for (i=0; (i+1)*sizeof(u64)<=size; i++) + fix_big_endian(&buf[i*sizeof(u64)], + sizeof(u64)); + res = ntfs_inode_set_times(ni, buf, size, flags); + } else + res = ntfs_inode_set_times(ni, value, size, flags); + break; + case XATTR_NTFS_CRTIME: + res = ntfs_inode_set_times(ni, value, + (size >= sizeof(u64) ? sizeof(u64) : size), flags); + break; + case XATTR_NTFS_CRTIME_BE: + if (value && (size >= sizeof(u64))) { + memcpy(buf,value,sizeof(u64)); + fix_big_endian(buf,sizeof(u64)); + res = ntfs_inode_set_times(ni, buf, sizeof(u64), flags); + } else + res = ntfs_inode_set_times(ni, value, size, flags); + break; + case XATTR_NTFS_EA : + res = ntfs_set_ntfs_ea(ni, value, size, flags); + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx, + enum SYSTEMXATTRS attr, + ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + + res = 0; + switch (attr) { + /* + * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES + * is never allowed + */ + case XATTR_NTFS_ACL : + case XATTR_NTFS_ATTRIB : + case XATTR_NTFS_ATTRIB_BE : + case XATTR_NTFS_EFSINFO : + case XATTR_NTFS_TIMES : + case XATTR_NTFS_TIMES_BE : + case XATTR_NTFS_CRTIME : + case XATTR_NTFS_CRTIME_BE : + res = -EPERM; + break; +#if POSIXACLS + case XATTR_POSIX_ACC : + case XATTR_POSIX_DEF : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_posix_acl(scx, ni, + (attr == XATTR_POSIX_ACC ? + nf_ns_xattr_posix_access : + nf_ns_xattr_posix_default))) + res = -errno; + } else + res = -errno; + break; +#endif + case XATTR_NTFS_REPARSE_DATA : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_ntfs_reparse_data(ni)) + res = -errno; + } else + res = -errno; + break; + case XATTR_NTFS_OBJECT_ID : + if (ni) { + if (!ntfs_allowed_as_owner(scx, ni) + || ntfs_remove_ntfs_object_id(ni)) + res = -errno; + } else + res = -errno; + break; + case XATTR_NTFS_DOS_NAME: + if (ni && dir_ni) { + if (ntfs_remove_ntfs_dos_name(ni,dir_ni)) + res = -errno; + /* ni and dir_ni have been closed */ + } else + res = -errno; + break; + case XATTR_NTFS_EA : + res = ntfs_remove_ntfs_ea(ni); + break; + default : + errno = EOPNOTSUPP; + res = -errno; + break; + } + return (res); +} + +#endif /* HAVE_SETXATTR */ diff --git a/libtool b/libtool new file mode 100755 index 0000000000000000000000000000000000000000..e0621560fef8937133312e3f781fcca8903189c6 --- /dev/null +++ b/libtool @@ -0,0 +1,10077 @@ +#! /bin/bash + +# libtool - Provide generalized library-building support services. +# Generated automatically by config.status (ntfs-3g) 2015.3.14 +# Libtool was configured on host cmc-HP-Compaq-Pro-6380-MT: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool 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. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# The names of the tagged configurations supported by this script. +available_tags="" + +# ### BEGIN LIBTOOL CONFIG + +# Which release of libtool.m4 was used? +macro_version=2.4.2 +macro_revision=1.3337 + +# Whether or not to build shared libraries. +build_libtool_libs=yes + +# Whether or not to build static libraries. +build_old_libs=yes + +# What type of objects to build. +pic_mode=default + +# Whether or not to optimize for fast installation. +fast_install=yes + +# Shell to use when invoking shell scripts. +SHELL="/bin/bash" + +# An echo program that protects backslashes. +ECHO="printf %s\\n" + +# The PATH separator for the build system. +PATH_SEPARATOR=":" + +# The host system. +host_alias= +host=x86_64-unknown-linux-gnu +host_os=linux-gnu + +# The build system. +build_alias= +build=x86_64-unknown-linux-gnu +build_os=linux-gnu + +# A sed program that does not truncate output. +SED="/bin/sed" + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP="/bin/grep" + +# An ERE matcher. +EGREP="/bin/grep -E" + +# A literal string matcher. +FGREP="/bin/grep -F" + +# A BSD- or MS-compatible name lister. +NM="/usr/bin/nm -B" + +# Whether we need soft or hard links. +LN_S="ln -s" + +# What is the maximum length of a command? +max_cmd_len=1572864 + +# Object file suffix (normally "o"). +objext=o + +# Executable file suffix (normally ""). +exeext= + +# whether the shell understands "unset". +lt_unset=unset + +# turn spaces into newlines. +SP2NL="tr \\040 \\012" + +# turn newlines into spaces. +NL2SP="tr \\015\\012 \\040\\040" + +# convert $build file names to $host format. +to_host_file_cmd=func_convert_file_noop + +# convert $build files to toolchain format. +to_tool_file_cmd=func_convert_file_noop + +# An object symbol dumper. +OBJDUMP="objdump" + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method="pass_all" + +# Command to use when deplibs_check_method = "file_magic". +file_magic_cmd="\$MAGIC_CMD" + +# How to find potential files when deplibs_check_method = "file_magic". +file_magic_glob="" + +# Find potential files using nocaseglob when deplibs_check_method = "file_magic". +want_nocaseglob="no" + +# DLL creation program. +DLLTOOL="false" + +# Command to associate shared and link libraries. +sharedlib_from_linklib_cmd="printf %s\\n" + +# The archiver. +AR="ar" + +# Flags to create an archive. +AR_FLAGS="cru" + +# How to feed a file listing to the archiver. +archiver_list_spec="@" + +# A symbol stripping program. +STRIP="strip" + +# Commands used to install an old-style archive. +RANLIB="ranlib" +old_postinstall_cmds="chmod 644 \$oldlib~\$RANLIB \$tool_oldlib" +old_postuninstall_cmds="" + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=no + +# A C compiler. +LTCC="gcc" + +# LTCC compiler flags. +LTCFLAGS="-g -O2 -Wall" + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe="sed -n -e 's/^.*[ ]\\([ABCDGIRSTW][ABCDGIRSTW]*\\)[ ][ ]*\\([_A-Za-z][_A-Za-z0-9]*\\)\$/\\1 \\2 \\2/p' | sed '/ __gnu_lto/d'" + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl="sed -n -e 's/^T .* \\(.*\\)\$/extern int \\1();/p' -e 's/^[ABCDGIRSTW]* .* \\(.*\\)\$/extern char \\1;/p'" + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address="sed -n -e 's/^: \\([^ ]*\\)[ ]*\$/ {\\\"\\1\\\", (void *) 0},/p' -e 's/^[ABCDGIRSTW]* \\([^ ]*\\) \\([^ ]*\\)\$/ {\"\\2\", (void *) \\&\\2},/p'" + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \\([^ ]*\\)[ ]*\$/ {\\\"\\1\\\", (void *) 0},/p' -e 's/^[ABCDGIRSTW]* \\([^ ]*\\) \\(lib[^ ]*\\)\$/ {\"\\2\", (void *) \\&\\2},/p' -e 's/^[ABCDGIRSTW]* \\([^ ]*\\) \\([^ ]*\\)\$/ {\"lib\\2\", (void *) \\&\\2},/p'" + +# Specify filename containing input files for $NM. +nm_file_list_spec="@" + +# The root where to search for dependent libraries,and in which our libraries should be installed. +lt_sysroot= + +# The name of the directory that contains temporary libtool files. +objdir=.libs + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=file + +# Must we lock files when doing compilation? +need_locks="no" + +# Manifest tool. +MANIFEST_TOOL=":" + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL="" + +# Tool to change global to local symbols on Mac OS X. +NMEDIT="" + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO="" + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL="" + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64="" + +# Old archive suffix (normally "a"). +libext=a + +# Shared library suffix (normally ".so"). +shrext_cmds=".so" + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds="" + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink="PATH LD_LIBRARY_PATH LD_RUN_PATH GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" + +# Do we need the "lib" prefix for modules? +need_lib_prefix=no + +# Do we need a version for libraries? +need_version=no + +# Library versioning type. +version_type=linux + +# Shared library runtime path variable. +runpath_var=LD_RUN_PATH + +# Shared library path variable. +shlibpath_var=LD_LIBRARY_PATH + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=no + +# Format of library name prefix. +libname_spec="lib\$name" + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec="\${libname}\${release}\${shared_ext}\$versuffix \${libname}\${release}\${shared_ext}\$major \$libname\${shared_ext}" + +# The coded name of the library, if different from the real name. +soname_spec="\${libname}\${release}\${shared_ext}\$major" + +# Permission mode override for installation of shared libraries. +install_override_mode="" + +# Command to use after installation of a shared archive. +postinstall_cmds="" + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds="" + +# Commands used to finish a libtool library installation in a directory. +finish_cmds="PATH=\\\"\\\$PATH:/sbin\\\" ldconfig -n \$libdir" + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval="" + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=yes + +# Compile-time system search path for libraries. +sys_lib_search_path_spec="/usr/lib/gcc/x86_64-linux-gnu/4.8 /usr/lib/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib " + +# Run-time system search path for libraries. +sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib /usr/lib/x86_64-linux-gnu/libfakeroot /lib/i386-linux-gnu /usr/lib/i386-linux-gnu /lib/i686-linux-gnu /usr/lib/i686-linux-gnu /usr/local/lib /lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/mesa-egl /usr/lib/x86_64-linux-gnu/mesa /usr/lib/x86_64-linux-gnu/mir/clientplatform/mesa /lib32 /usr/lib32 /libx32 /usr/libx32 " + +# Whether dlopen is supported. +dlopen_support=unknown + +# Whether dlopen of programs is supported. +dlopen_self=unknown + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=unknown + +# Commands to strip libraries. +old_striplib="strip --strip-debug" +striplib="strip --strip-unneeded" + + +# The linker used to build libraries. +LD="/usr/bin/ld -m elf_x86_64" + +# How to create reloadable object files. +reload_flag=" -r" +reload_cmds="\$LD\$reload_flag -o \$output\$reload_objs" + +# Commands used to build an old-style archive. +old_archive_cmds="\$AR \$AR_FLAGS \$oldlib\$oldobjs~\$RANLIB \$tool_oldlib" + +# A language specific compiler. +CC="gcc" + +# Is the compiler the GNU compiler? +with_gcc=yes + +# Compiler flag to turn off builtin functions. +no_builtin_flag=" -fno-builtin" + +# Additional compiler flags for building library objects. +pic_flag=" -fPIC -DPIC" + +# How to pass a linker flag through the compiler. +wl="-Wl," + +# Compiler flag to prevent dynamic linking. +link_static_flag="-static" + +# Does compiler simultaneously support -c and -o options? +compiler_c_o="yes" + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=no + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=no + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec="\${wl}--export-dynamic" + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec="\${wl}--whole-archive\$convenience \${wl}--no-whole-archive" + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object="no" + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds="" + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds="" + +# Commands used to build a shared archive. +archive_cmds="\$CC -shared \$pic_flag \$libobjs \$deplibs \$compiler_flags \${wl}-soname \$wl\$soname -o \$lib" +archive_expsym_cmds="echo \\\"{ global:\\\" > \$output_objdir/\$libname.ver~ + cat \$export_symbols | sed -e \\\"s/\\\\(.*\\\\)/\\\\1;/\\\" >> \$output_objdir/\$libname.ver~ + echo \\\"local: *; };\\\" >> \$output_objdir/\$libname.ver~ + \$CC -shared \$pic_flag \$libobjs \$deplibs \$compiler_flags \${wl}-soname \$wl\$soname \${wl}-version-script \${wl}\$output_objdir/\$libname.ver -o \$lib" + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds="" +module_expsym_cmds="" + +# Whether we are building with GNU ld or not. +with_gnu_ld="yes" + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag="" + +# Flag that enforces no undefined symbols. +no_undefined_flag="" + +# Flag to hardcode $libdir into a binary during linking. +# This must work even if $libdir does not exist +hardcode_libdir_flag_spec="\${wl}-rpath \${wl}\$libdir" + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator="" + +# Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=no + +# Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting ${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=no + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=no + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=unsupported + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=no + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=no + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=unknown + +# Set to "yes" if exported symbols are required. +always_export_symbols=no + +# The commands to list exported symbols. +export_symbols_cmds="\$NM \$libobjs \$convenience | \$global_symbol_pipe | \$SED 's/.* //' | sort | uniq > \$export_symbols" + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms="_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*" + +# Symbols that must always be exported. +include_expsyms="" + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds="" + +# Commands necessary for finishing linking programs. +postlink_cmds="" + +# Specify filename containing input files. +file_list_spec="" + +# How to hardcode a shared library path into an executable. +hardcode_action=immediate + +# ### END LIBTOOL CONFIG + + +# libtool (GNU libtool) 2.4.2 +# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996 + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, +# 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool 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. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, +# or obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Usage: $progname [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 +# --mode=MODE use operation mode MODE +# --preserve-dup-deps don't remove duplicate dependency libraries +# --quiet, --silent don't print informational messages +# --no-quiet, --no-silent +# print informational messages (default) +# --no-warn don't display warning messages +# --tag=TAG use configuration variables from tag TAG +# -v, --verbose print more informational messages than default +# --no-verbose don't print the extra informational messages +# --version print version information +# -h, --help, --help-all print short, long, or detailed help message +# +# 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. When passed as first option, +# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. +# Try `$progname --help --mode=MODE' for a more detailed description of MODE. +# +# When reporting a bug, please describe a test case to reproduce it and +# include the following information: +# +# host-triplet: $host +# shell: $SHELL +# compiler: $LTCC +# compiler flags: $LTCFLAGS +# linker: $LD (gnu? $with_gnu_ld) +# $progname: (GNU libtool) 2.4.2 +# automake: $automake_version +# autoconf: $autoconf_version +# +# Report bugs to <bug-libtool@gnu.org>. +# GNU libtool home page: <http://www.gnu.org/software/libtool/>. +# General help using GNU software: <http://www.gnu.org/gethelp/>. + +PROGRAM=libtool +PACKAGE=libtool +VERSION=2.4.2 +TIMESTAMP="" +package_revision=1.3337 + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# NLS nuisances: We save the old values to restore during execute mode. +lt_user_locale= +lt_safe_locale= +for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test \"\${$lt_var+set}\" = set; then + save_$lt_var=\$$lt_var + $lt_var=C + export $lt_var + lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" + lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" + fi" +done +LC_ALL=C +LANGUAGE=C +export LANGUAGE LC_ALL + +$lt_unset CDPATH + + +# 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" + + + +: ${CP="cp -f"} +test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} +: ${Xsed="$SED -e 1s/^X//"} + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +exit_status=$EXIT_SUCCESS + +# Make sure IFS has a sensible default +lt_nl=' +' +IFS=" $lt_nl" + +dirname="s,/[^/]*$,," +basename="s,^.*/,," + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac +} # Extended-shell func_dirname implementation + + +# func_basename file +func_basename () +{ + func_basename_result="${1##*/}" +} # Extended-shell func_basename implementation + + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac + func_basename_result="${1##*/}" +} # Extended-shell func_dirname_and_basename implementation + + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# func_strip_suffix prefix name +func_stripname () +{ + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary parameter first. + func_stripname_result=${3} + func_stripname_result=${func_stripname_result#"${1}"} + func_stripname_result=${func_stripname_result%"${2}"} +} # Extended-shell func_stripname implementation + + +# These SED scripts presuppose an absolute path with a trailing slash. +pathcar='s,^/\([^/]*\).*$,\1,' +pathcdr='s,^/[^/]*,,' +removedotparts=':dotsl + s@/\./@/@g + t dotsl + s,/\.$,/,' +collapseslashes='s@/\{1,\}@/@g' +finalslash='s,/*$,/,' + +# func_normal_abspath PATH +# Remove doubled-up and trailing slashes, "." path components, +# and cancel out any ".." path components in PATH after making +# it an absolute path. +# value returned in "$func_normal_abspath_result" +func_normal_abspath () +{ + # Start from root dir and reassemble the path. + func_normal_abspath_result= + func_normal_abspath_tpath=$1 + func_normal_abspath_altnamespace= + case $func_normal_abspath_tpath in + "") + # Empty path, that just means $cwd. + func_stripname '' '/' "`pwd`" + func_normal_abspath_result=$func_stripname_result + return + ;; + # The next three entries are used to spot a run of precisely + # two leading slashes without using negated character classes; + # we take advantage of case's first-match behaviour. + ///*) + # Unusual form of absolute path, do nothing. + ;; + //*) + # Not necessarily an ordinary path; POSIX reserves leading '//' + # and for example Cygwin uses it to access remote file shares + # over CIFS/SMB, so we conserve a leading double slash if found. + func_normal_abspath_altnamespace=/ + ;; + /*) + # Absolute path, do nothing. + ;; + *) + # Relative path, prepend $cwd. + func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath + ;; + esac + # Cancel out all the simple stuff to save iterations. We also want + # the path to end with a slash for ease of parsing, so make sure + # there is one (and only one) here. + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` + while :; do + # Processed it all yet? + if test "$func_normal_abspath_tpath" = / ; then + # If we ascended to the root using ".." the result may be empty now. + if test -z "$func_normal_abspath_result" ; then + func_normal_abspath_result=/ + fi + break + fi + func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcar"` + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcdr"` + # Figure out what to do with it + case $func_normal_abspath_tcomponent in + "") + # Trailing empty path component, ignore it. + ;; + ..) + # Parent dir; strip last assembled component from result. + func_dirname "$func_normal_abspath_result" + func_normal_abspath_result=$func_dirname_result + ;; + *) + # Actual path component, append it. + func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent + ;; + esac + done + # Restore leading double-slash if one was found on entry. + func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result +} + +# func_relative_path SRCDIR DSTDIR +# generates a relative path from SRCDIR to DSTDIR, with a trailing +# slash if non-empty, suitable for immediately appending a filename +# without needing to append a separator. +# value returned in "$func_relative_path_result" +func_relative_path () +{ + func_relative_path_result= + func_normal_abspath "$1" + func_relative_path_tlibdir=$func_normal_abspath_result + func_normal_abspath "$2" + func_relative_path_tbindir=$func_normal_abspath_result + + # Ascend the tree starting from libdir + while :; do + # check if we have found a prefix of bindir + case $func_relative_path_tbindir in + $func_relative_path_tlibdir) + # found an exact match + func_relative_path_tcancelled= + break + ;; + $func_relative_path_tlibdir*) + # found a matching prefix + func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" + func_relative_path_tcancelled=$func_stripname_result + if test -z "$func_relative_path_result"; then + func_relative_path_result=. + fi + break + ;; + *) + func_dirname $func_relative_path_tlibdir + func_relative_path_tlibdir=${func_dirname_result} + if test "x$func_relative_path_tlibdir" = x ; then + # Have to descend all the way to the root! + func_relative_path_result=../$func_relative_path_result + func_relative_path_tcancelled=$func_relative_path_tbindir + break + fi + func_relative_path_result=../$func_relative_path_result + ;; + esac + done + + # Now calculate path; take care to avoid doubling-up slashes. + func_stripname '' '/' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + func_stripname '/' '/' "$func_relative_path_tcancelled" + if test "x$func_stripname_result" != x ; then + func_relative_path_result=${func_relative_path_result}/${func_stripname_result} + fi + + # Normalisation. If bindir is libdir, return empty string, + # else relative path ending with a slash; either way, target + # file name can be directly appended. + if test ! -z "$func_relative_path_result"; then + func_stripname './' '' "$func_relative_path_result/" + func_relative_path_result=$func_stripname_result + fi +} + +# The name of this program: +func_dirname_and_basename "$progpath" +progname=$func_basename_result + +# Make sure we have an absolute path for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=$func_dirname_result + progdir=`cd "$progdir" && pwd` + progpath="$progdir/$progname" + ;; + *) + save_IFS="$IFS" + IFS=${PATH_SEPARATOR-:} + for progdir in $PATH; do + IFS="$save_IFS" + test -x "$progdir/$progname" && break + done + IFS="$save_IFS" + test -n "$progdir" || progdir=`pwd` + progpath="$progdir/$progname" + ;; +esac + +# 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='s/\([`"$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution that turns a string into a regex matching for the +# string literally. +sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' + +# Sed substitution that converts a w32 file name or path +# which contains forward slashes, into one that contains +# (escaped) backslashes. A very naive implementation. +lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + +# Re-`\' parameter expansions in output of double_quote_subst that were +# `\'-ed in input to the same. If an odd number of `\' preceded a '$' +# in input to double_quote_subst, that '$' was protected from expansion. +# Since each input `\' is now two `\'s, look for any number of runs of +# four `\'s followed by two `\'s and then a '$'. `\' that '$'. +bs='\\' +bs2='\\\\' +bs4='\\\\\\\\' +dollar='\$' +sed_double_backslash="\ + s/$bs4/&\\ +/g + s/^$bs2$dollar/$bs&/ + s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g + s/\n//g" + +# Standard options: +opt_dry_run=false +opt_help=false +opt_quiet=false +opt_verbose=false +opt_warning=: + +# func_echo arg... +# Echo program name prefixed message, along with the current mode +# name if it has been set yet. +func_echo () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }$*" +} + +# func_verbose arg... +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $opt_verbose && func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +# func_error arg... +# Echo program name prefixed message to standard error. +func_error () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 +} + +# func_warning arg... +# Echo program name prefixed warning message to standard error. +func_warning () +{ + $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 + + # bash bug again: + : +} + +# func_fatal_error arg... +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + +# func_fatal_help arg... +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + func_error ${1+"$@"} + func_fatal_error "$help" +} +help="Try \`$progname --help' for more information." ## default + + +# func_grep expression filename +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_mkdir_p directory-path +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + my_directory_path="$1" + my_dir_list= + + if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then + + # Protect directory names starting with `-' + case $my_directory_path in + -*) my_directory_path="./$my_directory_path" ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$my_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + my_dir_list="$my_directory_path:$my_dir_list" + + # If the last portion added has no slash in it, the list is done + case $my_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` + done + my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` + + save_mkdir_p_IFS="$IFS"; IFS=':' + for my_dir in $my_dir_list; do + IFS="$save_mkdir_p_IFS" + # mkdir can fail with a `File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$my_dir" 2>/dev/null || : + done + IFS="$save_mkdir_p_IFS" + + # Bail out if we (or some other process) failed to create a directory. + test -d "$my_directory_path" || \ + func_fatal_error "Failed to create \`$1'" + fi +} + + +# func_mktempdir [string] +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, STRING is the basename for that directory. +func_mktempdir () +{ + my_template="${TMPDIR-/tmp}/${1-$progname}" + + if test "$opt_dry_run" = ":"; then + # Return a directory name, but don't create it in dry-run mode + my_tmpdir="${my_template}-$$" + else + + # If mktemp works, use that first and foremost + my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` + + if test ! -d "$my_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" + + save_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$my_tmpdir" + umask $save_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$my_tmpdir" || \ + func_fatal_error "cannot create temporary directory \`$my_tmpdir'" + fi + + $ECHO "$my_tmpdir" +} + + +# func_quote_for_eval arg +# Aesthetically quote ARG to be evaled later. +# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT +# is double-quoted, suitable for a subsequent eval, whereas +# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters +# which are still active within double quotes backslashified. +func_quote_for_eval () +{ + case $1 in + *[\\\`\"\$]*) + func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; + *) + func_quote_for_eval_unquoted_result="$1" ;; + esac + + case $func_quote_for_eval_unquoted_result in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and and variable + # expansion for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" + ;; + *) + func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" + esac +} + + +# func_quote_for_expand arg +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + case $1 in + *[\\\`\"]*) + my_arg=`$ECHO "$1" | $SED \ + -e "$double_quote_subst" -e "$sed_double_backslash"` ;; + *) + my_arg="$1" ;; + esac + + case $my_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + my_arg="\"$my_arg\"" + ;; + esac + + func_quote_for_expand_result="$my_arg" +} + + +# func_show_eval cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$my_cmd" + my_status=$? + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + + +# func_show_eval_locale cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$lt_user_locale + $my_cmd" + my_status=$? + eval "$lt_safe_locale" + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + +# func_tr_sh +# Turn $1 into a string suitable for a shell variable name. +# Result is stored in $func_tr_sh_result. All characters +# not in the set a-zA-Z0-9_ are replaced with '_'. Further, +# if $1 begins with a digit, a '_' is prepended as well. +func_tr_sh () +{ + case $1 in + [0-9]* | *[!a-zA-Z0-9_]*) + func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` + ;; + * ) + func_tr_sh_result=$1 + ;; + esac +} + + +# func_version +# Echo version message to standard output and exit. +func_version () +{ + $opt_debug + + $SED -n '/(C)/!b go + :more + /\./!{ + N + s/\n# / / + b more + } + :go + /^# '$PROGRAM' (GNU /,/# warranty; / { + s/^# // + s/^# *$// + s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ + p + }' < "$progpath" + exit $? +} + +# func_usage +# Echo short help message to standard output and exit. +func_usage () +{ + $opt_debug + + $SED -n '/^# Usage:/,/^# *.*--help/ { + s/^# // + s/^# *$// + s/\$progname/'$progname'/ + p + }' < "$progpath" + echo + $ECHO "run \`$progname --help | more' for full usage" + exit $? +} + +# func_help [NOEXIT] +# Echo long help message to standard output and exit, +# unless 'noexit' is passed as argument. +func_help () +{ + $opt_debug + + $SED -n '/^# Usage:/,/# Report bugs to/ { + :print + s/^# // + s/^# *$// + s*\$progname*'$progname'* + s*\$host*'"$host"'* + s*\$SHELL*'"$SHELL"'* + s*\$LTCC*'"$LTCC"'* + s*\$LTCFLAGS*'"$LTCFLAGS"'* + s*\$LD*'"$LD"'* + s/\$with_gnu_ld/'"$with_gnu_ld"'/ + s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/ + s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/ + p + d + } + /^# .* home page:/b print + /^# General help using/b print + ' < "$progpath" + ret=$? + if test -z "$1"; then + exit $ret + fi +} + +# func_missing_arg argname +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + $opt_debug + + func_error "missing argument for $1." + exit_cmd=exit +} + + +# func_split_short_opt shortopt +# Set func_split_short_opt_name and func_split_short_opt_arg shell +# variables after splitting SHORTOPT after the 2nd character. +func_split_short_opt () +{ + func_split_short_opt_arg=${1#??} + func_split_short_opt_name=${1%"$func_split_short_opt_arg"} +} # Extended-shell func_split_short_opt implementation + + +# func_split_long_opt longopt +# Set func_split_long_opt_name and func_split_long_opt_arg shell +# variables after splitting LONGOPT at the `=' sign. +func_split_long_opt () +{ + func_split_long_opt_name=${1%%=*} + func_split_long_opt_arg=${1#*=} +} # Extended-shell func_split_long_opt implementation + +exit_cmd=: + + + + + +magic="%%%MAGIC variable%%%" +magic_exe="%%%MAGIC EXE variable%%%" + +# Global variables. +nonopt= +preserve_args= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" +extracted_archives= +extracted_serial=0 + +# 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= + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "${1}+=\${2}" +} # Extended-shell func_append implementation + +# func_append_quoted var value +# Quote VALUE and append to the end of shell variable VAR, separated +# by a space. +func_append_quoted () +{ + func_quote_for_eval "${2}" + eval "${1}+=\\ \$func_quote_for_eval_result" +} # Extended-shell func_append_quoted implementation + + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=$(( $* )) +} # Extended-shell func_arith implementation + + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=${#1} +} # Extended-shell func_len implementation + + +# func_lo2o object +func_lo2o () +{ + case ${1} in + *.lo) func_lo2o_result=${1%.lo}.${objext} ;; + *) func_lo2o_result=${1} ;; + esac +} # Extended-shell func_lo2o implementation + + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=${1%.*}.lo +} # Extended-shell func_xform implementation + + +# func_fatal_configuration arg... +# Echo program name prefixed message to standard error, followed by +# a configuration failure hint, and exit. +func_fatal_configuration () +{ + func_error ${1+"$@"} + func_error "See the $PACKAGE documentation for more information." + func_fatal_error "Fatal configuration error." +} + + +# func_config +# Display the configuration for all the tags in this script. +func_config () +{ + re_begincf='^# ### BEGIN LIBTOOL' + re_endcf='^# ### END LIBTOOL' + + # Default configuration. + $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" + + # Now print the configurations for the tags. + for tagname in $taglist; do + $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" + done + + exit $? +} + +# func_features +# Display the features supported by this script. +func_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 $? +} + +# func_enable_tag tagname +# Verify that TAGNAME is valid, and either flag an error and exit, or +# enable the TAGNAME tag. We also add TAGNAME to the global $taglist +# variable here. +func_enable_tag () +{ + # Global variable: + tagname="$1" + + re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" + re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" + sed_extractcf="/$re_begincf/,/$re_endcf/p" + + # Validate tagname. + case $tagname in + *[!-_A-Za-z0-9,/]*) + func_fatal_error "invalid tag name: $tagname" + ;; + esac + + # Don't test for the "default" C tag, as we know it's + # there but not specially marked. + case $tagname in + CC) ;; + *) + if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then + taglist="$taglist $tagname" + + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + +# func_check_version_match +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + fi + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +# Shorthand for --mode=foo, only valid as the first argument +case $1 in +clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; +compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; +execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; +finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; +install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; +link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; +uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift + ;; +esac + + + +# Option defaults: +opt_debug=: +opt_dry_run=false +opt_config=false +opt_preserve_dup_deps=false +opt_features=false +opt_finish=false +opt_help=false +opt_help_all=false +opt_silent=: +opt_warning=: +opt_verbose=: +opt_silent=false +opt_verbose=false + + +# Parse options once, thoroughly. This comes as soon as possible in the +# script to make things like `--version' happen as quickly as we can. +{ + # this just eases exit handling + while test $# -gt 0; do + opt="$1" + shift + case $opt in + --debug|-x) opt_debug='set -x' + func_echo "enabling shell trace mode" + $opt_debug + ;; + --dry-run|--dryrun|-n) + opt_dry_run=: + ;; + --config) + opt_config=: +func_config + ;; + --dlopen|-dlopen) + optarg="$1" + opt_dlopen="${opt_dlopen+$opt_dlopen +}$optarg" + shift + ;; + --preserve-dup-deps) + opt_preserve_dup_deps=: + ;; + --features) + opt_features=: +func_features + ;; + --finish) + opt_finish=: +set dummy --mode finish ${1+"$@"}; shift + ;; + --help) + opt_help=: + ;; + --help-all) + opt_help_all=: +opt_help=': help-all' + ;; + --mode) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_mode="$optarg" +case $optarg in + # Valid mode arguments: + clean|compile|execute|finish|install|link|relink|uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $opt" + exit_cmd=exit + break + ;; +esac + shift + ;; + --no-silent|--no-quiet) + opt_silent=false +preserve_args+=" $opt" + ;; + --no-warning|--no-warn) + opt_warning=false +preserve_args+=" $opt" + ;; + --no-verbose) + opt_verbose=false +preserve_args+=" $opt" + ;; + --silent|--quiet) + opt_silent=: +preserve_args+=" $opt" + opt_verbose=false + ;; + --verbose|-v) + opt_verbose=: +preserve_args+=" $opt" +opt_silent=false + ;; + --tag) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_tag="$optarg" +preserve_args+=" $opt $optarg" +func_enable_tag "$optarg" + shift + ;; + + -\?|-h) func_usage ;; + --help) func_help ;; + --version) func_version ;; + + # Separate optargs to long options: + --*=*) + func_split_long_opt "$opt" + set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} + shift + ;; + + # Separate non-argument short options: + -\?*|-h*|-n*|-v*) + func_split_short_opt "$opt" + set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + --) break ;; + -*) func_fatal_help "unrecognized option \`$opt'" ;; + *) set dummy "$opt" ${1+"$@"}; shift; break ;; + esac + done + + # Validate options: + + # save first non-option argument + if test "$#" -gt 0; then + nonopt="$opt" + shift + fi + + # preserve --debug + test "$opt_debug" = : || preserve_args+=" --debug" + + case $host in + *cygwin* | *mingw* | *pw32* | *cegcc*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + ;; + esac + + $opt_help || { + # Sanity checks first: + func_check_version_match + + if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + func_fatal_configuration "not configured to build any kind of library" + fi + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$opt_dlopen" && test "$opt_mode" != execute; then + func_error "unrecognized option \`-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$progname --help --mode=$opt_mode' for more information." + } + + + # Bail if the options were screwed + $exit_cmd $EXIT_FAILURE +} + + + + +## ----------- ## +## Main. ## +## ----------- ## + +# func_lalib_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null \ + | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + +# func_lalib_unsafe_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if `file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case "$lalib_p_line" in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test "$lalib_p" = yes +} + +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + func_lalib_p "$1" +} + +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} + +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" +} + +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} + + +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $opt_debug + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$save_ifs + eval cmd=\"$cmd\" + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# `FILE.' does not work on cygwin managed mounts. +func_source () +{ + $opt_debug + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_resolve_sysroot PATH +# Replace a leading = in PATH with a sysroot. Store the result into +# func_resolve_sysroot_result +func_resolve_sysroot () +{ + func_resolve_sysroot_result=$1 + case $func_resolve_sysroot_result in + =*) + func_stripname '=' '' "$func_resolve_sysroot_result" + func_resolve_sysroot_result=$lt_sysroot$func_stripname_result + ;; + esac +} + +# func_replace_sysroot PATH +# If PATH begins with the sysroot, replace it with = and +# store the result into func_replace_sysroot_result. +func_replace_sysroot () +{ + case "$lt_sysroot:$1" in + ?*:"$lt_sysroot"*) + func_stripname "$lt_sysroot" '' "$1" + func_replace_sysroot_result="=$func_stripname_result" + ;; + *) + # Including no sysroot. + func_replace_sysroot_result=$1 + ;; + esac +} + +# 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 () +{ + $opt_debug + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + 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 "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; + # 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. + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case "$@ " in + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) + # 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 + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with \`--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=${1} + if test "$build_libtool_libs" = yes; then + write_lobj=\'${2}\' + else + write_lobj=none + fi + + if test "$build_old_libs" = yes; then + write_oldobj=\'${3}\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T <<EOF +# $write_libobj - a libtool object file +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object=$write_lobj + +# Name of the non-PIC object +non_pic_object=$write_oldobj + +EOF + $MV "${write_libobj}T" "${write_libobj}" + } +} + + +################################################## +# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS # +################################################## + +# func_convert_core_file_wine_to_w32 ARG +# Helper function used by file name conversion functions when $build is *nix, +# and $host is mingw, cygwin, or some other w32 environment. Relies on a +# correctly configured wine environment available, with the winepath program +# in $build's $PATH. +# +# ARG is the $build file name to be converted to w32 format. +# Result is available in $func_convert_core_file_wine_to_w32_result, and will +# be empty on error (or when ARG is empty) +func_convert_core_file_wine_to_w32 () +{ + $opt_debug + func_convert_core_file_wine_to_w32_result="$1" + if test -n "$1"; then + # Unfortunately, winepath does not exit with a non-zero error code, so we + # are forced to check the contents of stdout. On the other hand, if the + # command is not found, the shell will set an exit code of 127 and print + # *an error message* to stdout. So we must check for both error code of + # zero AND non-empty stdout, which explains the odd construction: + func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null` + if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then + func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | + $SED -e "$lt_sed_naive_backslashify"` + else + func_convert_core_file_wine_to_w32_result= + fi + fi +} +# end: func_convert_core_file_wine_to_w32 + + +# func_convert_core_path_wine_to_w32 ARG +# Helper function used by path conversion functions when $build is *nix, and +# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly +# configured wine environment available, with the winepath program in $build's +# $PATH. Assumes ARG has no leading or trailing path separator characters. +# +# ARG is path to be converted from $build format to win32. +# Result is available in $func_convert_core_path_wine_to_w32_result. +# Unconvertible file (directory) names in ARG are skipped; if no directory names +# are convertible, then the result may be empty. +func_convert_core_path_wine_to_w32 () +{ + $opt_debug + # unfortunately, winepath doesn't convert paths, only file names + func_convert_core_path_wine_to_w32_result="" + if test -n "$1"; then + oldIFS=$IFS + IFS=: + for func_convert_core_path_wine_to_w32_f in $1; do + IFS=$oldIFS + func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" + if test -n "$func_convert_core_file_wine_to_w32_result" ; then + if test -z "$func_convert_core_path_wine_to_w32_result"; then + func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" + else + func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" + fi + fi + done + IFS=$oldIFS + fi +} +# end: func_convert_core_path_wine_to_w32 + + +# func_cygpath ARGS... +# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when +# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) +# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or +# (2), returns the Cygwin file name or path in func_cygpath_result (input +# file name or path is assumed to be in w32 format, as previously converted +# from $build's *nix or MSYS format). In case (3), returns the w32 file name +# or path in func_cygpath_result (input file name or path is assumed to be in +# Cygwin format). Returns an empty string on error. +# +# ARGS are passed to cygpath, with the last one being the file name or path to +# be converted. +# +# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH +# environment variable; do not put it in $PATH. +func_cygpath () +{ + $opt_debug + if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then + func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` + if test "$?" -ne 0; then + # on failure, ensure result is empty + func_cygpath_result= + fi + else + func_cygpath_result= + func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" + fi +} +#end: func_cygpath + + +# func_convert_core_msys_to_w32 ARG +# Convert file name or path ARG from MSYS format to w32 format. Return +# result in func_convert_core_msys_to_w32_result. +func_convert_core_msys_to_w32 () +{ + $opt_debug + # awkward: cmd appends spaces to result + func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | + $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` +} +#end: func_convert_core_msys_to_w32 + + +# func_convert_file_check ARG1 ARG2 +# Verify that ARG1 (a file name in $build format) was converted to $host +# format in ARG2. Otherwise, emit an error message, but continue (resetting +# func_to_host_file_result to ARG1). +func_convert_file_check () +{ + $opt_debug + if test -z "$2" && test -n "$1" ; then + func_error "Could not determine host file name corresponding to" + func_error " \`$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_file_result="$1" + fi +} +# end func_convert_file_check + + +# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH +# Verify that FROM_PATH (a path in $build format) was converted to $host +# format in TO_PATH. Otherwise, emit an error message, but continue, resetting +# func_to_host_file_result to a simplistic fallback value (see below). +func_convert_path_check () +{ + $opt_debug + if test -z "$4" && test -n "$3"; then + func_error "Could not determine the host path corresponding to" + func_error " \`$3'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This is a deliberately simplistic "conversion" and + # should not be "improved". See libtool.info. + if test "x$1" != "x$2"; then + lt_replace_pathsep_chars="s|$1|$2|g" + func_to_host_path_result=`echo "$3" | + $SED -e "$lt_replace_pathsep_chars"` + else + func_to_host_path_result="$3" + fi + fi +} +# end func_convert_path_check + + +# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG +# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT +# and appending REPL if ORIG matches BACKPAT. +func_convert_path_front_back_pathsep () +{ + $opt_debug + case $4 in + $1 ) func_to_host_path_result="$3$func_to_host_path_result" + ;; + esac + case $4 in + $2 ) func_to_host_path_result+="$3" + ;; + esac +} +# end func_convert_path_front_back_pathsep + + +################################################## +# $build to $host FILE NAME CONVERSION FUNCTIONS # +################################################## +# invoked via `$to_host_file_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# Result will be available in $func_to_host_file_result. + + +# func_to_host_file ARG +# Converts the file name ARG from $build format to $host format. Return result +# in func_to_host_file_result. +func_to_host_file () +{ + $opt_debug + $to_host_file_cmd "$1" +} +# end func_to_host_file + + +# func_to_tool_file ARG LAZY +# converts the file name ARG from $build format to toolchain format. Return +# result in func_to_tool_file_result. If the conversion in use is listed +# in (the comma separated) LAZY, no conversion takes place. +func_to_tool_file () +{ + $opt_debug + case ,$2, in + *,"$to_tool_file_cmd",*) + func_to_tool_file_result=$1 + ;; + *) + $to_tool_file_cmd "$1" + func_to_tool_file_result=$func_to_host_file_result + ;; + esac +} +# end func_to_tool_file + + +# func_convert_file_noop ARG +# Copy ARG to func_to_host_file_result. +func_convert_file_noop () +{ + func_to_host_file_result="$1" +} +# end func_convert_file_noop + + +# func_convert_file_msys_to_w32 ARG +# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_file_result. +func_convert_file_msys_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_to_host_file_result="$func_convert_core_msys_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_w32 + + +# func_convert_file_cygwin_to_w32 ARG +# Convert file name ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_file_cygwin_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # because $build is cygwin, we call "the" cygpath in $PATH; no need to use + # LT_CYGPATH in this case. + func_to_host_file_result=`cygpath -m "$1"` + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_cygwin_to_w32 + + +# func_convert_file_nix_to_w32 ARG +# Convert file name ARG from *nix to w32 format. Requires a wine environment +# and a working winepath. Returns result in func_to_host_file_result. +func_convert_file_nix_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_file_wine_to_w32 "$1" + func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_w32 + + +# func_convert_file_msys_to_cygwin ARG +# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_file_msys_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_cygpath -u "$func_convert_core_msys_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_cygwin + + +# func_convert_file_nix_to_cygwin ARG +# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed +# in a wine environment, working winepath, and LT_CYGPATH set. Returns result +# in func_to_host_file_result. +func_convert_file_nix_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. + func_convert_core_file_wine_to_w32 "$1" + func_cygpath -u "$func_convert_core_file_wine_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_cygwin + + +############################################# +# $build to $host PATH CONVERSION FUNCTIONS # +############################################# +# invoked via `$to_host_path_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# The result will be available in $func_to_host_path_result. +# +# Path separators are also converted from $build format to $host format. If +# ARG begins or ends with a path separator character, it is preserved (but +# converted to $host format) on output. +# +# All path conversion functions are named using the following convention: +# file name conversion function : func_convert_file_X_to_Y () +# path conversion function : func_convert_path_X_to_Y () +# where, for any given $build/$host combination the 'X_to_Y' value is the +# same. If conversion functions are added for new $build/$host combinations, +# the two new functions must follow this pattern, or func_init_to_host_path_cmd +# will break. + + +# func_init_to_host_path_cmd +# Ensures that function "pointer" variable $to_host_path_cmd is set to the +# appropriate value, based on the value of $to_host_file_cmd. +to_host_path_cmd= +func_init_to_host_path_cmd () +{ + $opt_debug + if test -z "$to_host_path_cmd"; then + func_stripname 'func_convert_file_' '' "$to_host_file_cmd" + to_host_path_cmd="func_convert_path_${func_stripname_result}" + fi +} + + +# func_to_host_path ARG +# Converts the path ARG from $build format to $host format. Return result +# in func_to_host_path_result. +func_to_host_path () +{ + $opt_debug + func_init_to_host_path_cmd + $to_host_path_cmd "$1" +} +# end func_to_host_path + + +# func_convert_path_noop ARG +# Copy ARG to func_to_host_path_result. +func_convert_path_noop () +{ + func_to_host_path_result="$1" +} +# end func_convert_path_noop + + +# func_convert_path_msys_to_w32 ARG +# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_path_result. +func_convert_path_msys_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from ARG. MSYS + # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; + # and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_msys_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_msys_to_w32 + + +# func_convert_path_cygwin_to_w32 ARG +# Convert path ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_path_cygwin_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_cygwin_to_w32 + + +# func_convert_path_nix_to_w32 ARG +# Convert path ARG from *nix to w32 format. Requires a wine environment and +# a working winepath. Returns result in func_to_host_file_result. +func_convert_path_nix_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_nix_to_w32 + + +# func_convert_path_msys_to_cygwin ARG +# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_path_msys_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_msys_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_msys_to_cygwin + + +# func_convert_path_nix_to_cygwin ARG +# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a +# a wine environment, working winepath, and LT_CYGPATH set. Returns result in +# func_to_host_file_result. +func_convert_path_nix_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_nix_to_cygwin + + +# func_mode_compile arg... +func_mode_compile () +{ + $opt_debug + # 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= + pie_flag= + + 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) + test -n "$libobj" && \ + func_fatal_error "you cannot specify \`-o' more than once" + arg_mode=target + continue + ;; + + -pie | -fpie | -fPIE) + pie_flag+=" $arg" + continue + ;; + + -shared | -static | -prefer-pic | -prefer-non-pic) + 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,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + func_append_quoted lastarg "$arg" + done + IFS="$save_ifs" + func_stripname ' ' '' "$lastarg" + lastarg=$func_stripname_result + + # Add the arguments to 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. + func_append_quoted base_compile "$lastarg" + done # for arg + + case $arg_mode in + arg) + func_fatal_error "you must specify an argument for -Xcompile" + ;; + target) + func_fatal_error "you must specify a target with \`-o'" + ;; + *) + # Get the name of the library object. + test -z "$libobj" && { + func_basename "$srcfile" + libobj="$func_basename_result" + } + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + case $libobj in + *.[cCFSifmso] | \ + *.ada | *.adb | *.ads | *.asm | \ + *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ + *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) + func_xform "$libobj" + libobj=$func_xform_result + ;; + esac + + case $libobj in + *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; + *) + func_fatal_error "cannot determine name of library object from \`$libobj'" + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + continue + ;; + + -static) + build_libtool_libs=no + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + func_quote_for_eval "$libobj" + test "X$libobj" != "X$func_quote_for_eval_result" \ + && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && func_warning "libobj name \`$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname="$func_basename_result" + xdir="$func_dirname_result" + lobj=${xdir}$objdir/$objname + + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" + + # 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 + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2* | cegcc*) + 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 "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + 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 $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "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." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + removelist+=" $output_obj" + $ECHO "$srcfile" > "$lockfile" + fi + + $opt_dry_run || $RM $removelist + removelist+=" $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + + func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 + srcfile=$func_to_tool_file_result + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result + + # Only build a PIC object if we are building libtool libraries. + if test "$build_libtool_libs" = yes; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test "$pic_mode" != no; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + func_mkdir_p "$xdir$objdir" + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + command+=" -o $lobj" + fi + + func_show_eval_locale "$command" \ + 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/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." + + $opt_dry_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 + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + + # Allow error messages only from the first compilation. + if test "$suppress_opt" = yes; then + suppress_output=' >/dev/null 2>&1' + fi + fi + + # Only build a position-dependent object if we build old libraries. + if test "$build_old_libs" = yes; then + if test "$pic_mode" != yes; then + # Don't build PIC code + command="$base_compile $qsrcfile$pie_flag" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test "$compiler_c_o" = yes; then + command+=" -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + command+="$suppress_output" + func_show_eval_locale "$command" \ + '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/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." + + $opt_dry_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 + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + fi + + $opt_dry_run || { + func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" + + # Unlock the critical section if it was locked + if test "$need_locks" != no; then + removelist=$lockfile + $RM "$lockfile" + fi + } + + exit $EXIT_SUCCESS +} + +$opt_help || { + test "$opt_mode" = compile && func_mode_compile ${1+"$@"} +} + +func_mode_help () +{ + # We need to display help for each of the modes. + case $opt_mode in + "") + # Generic help is extracted from the usage comments + # at the start of this file. + func_help + ;; + + clean) + $ECHO \ +"Usage: $progname [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: $progname [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 + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to build PIC objects only + -prefer-non-pic try to build non-PIC objects only + -shared do not build a \`.o' file suitable for static linking + -static only build a \`.o' file suitable for static linking + -Wc,FLAG pass FLAG directly to the compiler + +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: $progname [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: $progname [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: $progname [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 following components of INSTALL-COMMAND are treated specially: + + -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + + link) + $ECHO \ +"Usage: $progname [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 + -bindir BINDIR specify path to binaries directory (for systems where + libraries must be found in the PATH setting at runtime) + -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 + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface + -Wc,FLAG + -Xcompiler FLAG pass linker-specific FLAG directly to the compiler + -Wl,FLAG + -Xlinker FLAG pass linker-specific FLAG directly to the linker + -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) + +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: $progname [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." + ;; + + *) + func_fatal_help "invalid operation mode \`$opt_mode'" + ;; + esac + + echo + $ECHO "Try \`$progname --help' for more information about other modes." +} + +# Now that we've collected a possible --mode arg, show help if necessary +if $opt_help; then + if test "$opt_help" = :; then + func_mode_help + else + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + func_mode_help + done + } | sed -n '1p; 2,$s/^Usage:/ or: /p' + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + echo + func_mode_help + done + } | + sed '1d + /^When reporting/,/^Report/{ + H + d + } + $x + /information about other modes/d + /more detailed .*MODE/d + s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' + fi + exit $? +fi + + +# func_mode_execute arg... +func_mode_execute () +{ + $opt_debug + # The first argument is the command name. + cmd="$nonopt" + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" + + # Handle -dlopen flags immediately. + for file in $opt_dlopen; do + test -f "$file" \ + || func_fatal_help "\`$file' is not a file" + + dir= + case $file in + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$lib' is not a valid libtool archive" + + # Read the libtool library. + dlname= + library_names= + func_source "$file" + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "\`$file' was not linked with \`-export-dynamic'" + continue + fi + + func_dirname "$file" "" "." + dir="$func_dirname_result" + + if test -f "$dir/$objdir/$dlname"; then + dir+="/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" + fi + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir="$func_dirname_result" + ;; + + *) + func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" + 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 + -* | *.la | *.lo ) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file="$progdir/$program" + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_append_quoted args "$file" + done + + if test "X$opt_dry_run" = Xfalse; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done + + # 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 + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = execute && func_mode_execute ${1+"$@"} + + +# func_mode_finish arg... +func_mode_finish () +{ + $opt_debug + libs= + libdirs= + admincmds= + + for opt in "$nonopt" ${1+"$@"} + do + if test -d "$opt"; then + libdirs+=" $opt" + + elif test -f "$opt"; then + if func_lalib_unsafe_p "$opt"; then + libs+=" $opt" + else + func_warning "\`$opt' is not a valid libtool archive" + fi + + else + func_fatal_error "invalid argument \`$opt'" + fi + done + + if test -n "$libs"; then + if test -n "$lt_sysroot"; then + sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` + sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" + else + sysroot_cmd= + fi + + # Remove sysroot references + if $opt_dry_run; then + for lib in $libs; do + echo "removing references to $lt_sysroot and \`=' prefixes from $lib" + done + else + tmpdir=`func_mktempdir` + for lib in $libs; do + sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ + > $tmpdir/tmp-la + mv -f $tmpdir/tmp-la $lib + done + ${RM}r "$tmpdir" + fi + fi + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || admincmds+=" + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + $opt_silent && exit $EXIT_SUCCESS + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + 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" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" + echo "pages." + ;; + *) + echo "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + echo "----------------------------------------------------------------------" + fi + exit $EXIT_SUCCESS +} + +test "$opt_mode" = finish && func_mode_finish ${1+"$@"} + + +# func_mode_install arg... +func_mode_install () +{ + $opt_debug + # 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. + case $nonopt in *shtool*) :;; *) false;; esac; then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + install_prog+="$func_quote_for_eval_result" + install_shared_prog=$install_prog + case " $install_prog " in + *[\\\ /]cp\ *) install_cp=: ;; + *) install_cp=false ;; + esac + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + no_mode=: + for arg + do + arg2= + if test -n "$dest"; then + files+=" $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) + if $install_cp; then :; else + prev=$arg + fi + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + if test "x$prev" = x-m && test -n "$install_override_mode"; then + arg2=$install_override_mode + no_mode=false + fi + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + install_prog+=" $func_quote_for_eval_result" + if test -n "$arg2"; then + func_quote_for_eval "$arg2" + fi + install_shared_prog+=" $func_quote_for_eval_result" + done + + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" + + test -n "$prev" && \ + func_fatal_help "the \`$prev' option requires an argument" + + if test -n "$install_override_mode" && $no_mode; then + if $install_cp; then :; else + func_quote_for_eval "$install_override_mode" + install_shared_prog+=" -m $func_quote_for_eval_result" + fi + fi + + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" + fi + fi + + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir="$func_dirname_result" + destname="$func_basename_result" + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "\`$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "\`$destdir' must be an absolute directory name" + ;; + 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+=" $file" + ;; + + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # 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+=" $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) future_libdirs+=" $libdir" ;; + esac + fi + + func_dirname "$file" "/" "" + dir="$func_dirname_result" + 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 -e "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. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" + + 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 + + func_warning "relinking \`$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname="$1" + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme="$stripme" + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme="" + ;; + esac + ;; + esac + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try `ln -sf' first, because the `ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + func_execute_cmds "$postinstall_cmds" 'exit $?' + fi + + # Install the pseudo-library for information purposes. + func_basename "$file" + name="$func_basename_result" + instname="$dir/$name"i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' + + # Maybe install the static library, too. + test -n "$old_library" && 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 + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to \`$destfile'" + ;; + esac + + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_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 + func_basename "$file" + destfile="$func_basename_result" + 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 + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= + + func_source "$wrapper" + + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script \`$wrapper'" + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "\`$lib' has not been installed in \`$libdir'" + finalize=no + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + $opt_dry_run || { + if test "$finalize" = yes; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file="$func_basename_result" + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_silent || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink \`$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file="$outputname" + else + func_warning "cannot relink \`$file'" + fi + } + else + # Install the binary that we compiled earlier. + file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac + ;; + esac + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done + + for file in $staticlibs; do + func_basename "$file" + name="$func_basename_result" + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $tool_oldlib" 'exit $?' + fi + + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done + + test -n "$future_libdirs" && \ + func_warning "remember to run \`$progname --finish$future_libdirs'" + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = install && func_mode_install ${1+"$@"} + + +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $opt_debug + my_outputname="$1" + my_originator="$2" + my_pic_p="${3-no}" + my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms="${my_outputname}S.c" + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi + + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${my_outputname}.nm" + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) +#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" +#endif + +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + func_verbose "generating symbol list for \`$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_to_tool_file "$progfile" func_convert_file_msys_to_w32 + func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" + $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$outputname.exp" + $opt_dry_run || { + $RM $export_symbols + eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + } + else + $opt_dry_run || { + eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + } + fi + fi + + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from \`$dlprefile'" + func_basename "$dlprefile" + name="$func_basename_result" + case $host in + *cygwin* | *mingw* | *cegcc* ) + # if an import library, we need to obtain dlname + if func_win32_import_lib_p "$dlprefile"; then + func_tr_sh "$dlprefile" + eval "curr_lafile=\$libfile_$func_tr_sh_result" + dlprefile_dlbasename="" + if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then + # Use subshell, to avoid clobbering current variable values + dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` + if test -n "$dlprefile_dlname" ; then + func_basename "$dlprefile_dlname" + dlprefile_dlbasename="$func_basename_result" + else + # no lafile. user explicitly requested -dlpreopen <import library>. + $sharedlib_from_linklib_cmd "$dlprefile" + dlprefile_dlbasename=$sharedlib_from_linklib_result + fi + fi + $opt_dry_run || { + if test -n "$dlprefile_dlbasename" ; then + eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' + else + func_warning "Could not compute DLL name from $name" + eval '$ECHO ": $name " >> "$nlist"' + fi + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | + $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" + } + else # not an import lib + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + fi + ;; + *) + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + ;; + esac + done + + $opt_dry_run || { + # 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 >/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/$my_dlsyms"' + else + echo '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi + + echo >> "$output_objdir/$my_dlsyms" "\ + +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +extern LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[]; +LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{\ + { \"$my_originator\", (void *) 0 }," + + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + echo >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + 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*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + if test "X$my_pic_p" != Xno; then + pic_flag_for_symtable=" $pic_flag" + fi + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) symtab_cflags+=" $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' + + # Transform the symbol file into the correct name. + symfileobj="$output_objdir/${my_outputname}S.$objext" + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + else + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + fi + ;; + *) + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for \`$my_dlsyms'" + ;; + 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 "$compile_command" | $SED "s% @SYMFILE@%%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` + fi +} + +# 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. +# Despite the name, also deal with 64 bit binaries. +func_win32_libid () +{ + $opt_debug + 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 + # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then + func_to_tool_file "$1" func_convert_file_msys_to_w32 + win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | + $SED -n -e ' + 1,100{ + / I /{ + s,.*,import, + p + q + } + }'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + 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_cygming_dll_for_implib ARG +# +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib () +{ + $opt_debug + sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` +} + +# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs +# +# The is the core of a fallback implementation of a +# platform-specific function to extract the name of the +# DLL associated with the specified import library LIBNAME. +# +# SECTION_NAME is either .idata$6 or .idata$7, depending +# on the platform and compiler that created the implib. +# +# Echos the name of the DLL associated with the +# specified import library. +func_cygming_dll_for_implib_fallback_core () +{ + $opt_debug + match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` + $OBJDUMP -s --section "$1" "$2" 2>/dev/null | + $SED '/^Contents of section '"$match_literal"':/{ + # Place marker at beginning of archive member dllname section + s/.*/====MARK====/ + p + d + } + # These lines can sometimes be longer than 43 characters, but + # are always uninteresting + /:[ ]*file format pe[i]\{,1\}-/d + /^In archive [^:]*:/d + # Ensure marker is printed + /^====MARK====/p + # Remove all lines with less than 43 characters + /^.\{43\}/!d + # From remaining lines, remove first 43 characters + s/^.\{43\}//' | + $SED -n ' + # Join marker and all lines until next marker into a single line + /^====MARK====/ b para + H + $ b para + b + :para + x + s/\n//g + # Remove the marker + s/^====MARK====// + # Remove trailing dots and whitespace + s/[\. \t]*$// + # Print + /./p' | + # we now have a list, one entry per line, of the stringified + # contents of the appropriate section of all members of the + # archive which possess that section. Heuristic: eliminate + # all those which have a first or second character that is + # a '.' (that is, objdump's representation of an unprintable + # character.) This should work for all archives with less than + # 0x302f exports -- but will fail for DLLs whose name actually + # begins with a literal '.' or a single character followed by + # a '.'. + # + # Of those that remain, print the first one. + $SED -e '/^\./d;/^.\./d;q' +} + +# func_cygming_gnu_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is a GNU/binutils-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_gnu_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` + test -n "$func_cygming_gnu_implib_tmp" +} + +# func_cygming_ms_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is an MS-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_ms_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` + test -n "$func_cygming_ms_implib_tmp" +} + +# func_cygming_dll_for_implib_fallback ARG +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# +# This fallback implementation is for use when $DLLTOOL +# does not support the --identify-strict option. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib_fallback () +{ + $opt_debug + if func_cygming_gnu_implib_p "$1" ; then + # binutils import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` + elif func_cygming_ms_implib_p "$1" ; then + # ms-generated import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` + else + # unknown + sharedlib_from_linklib_result="" + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $opt_debug + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + if test "$lock_old_archive_extraction" = yes; then + lockfile=$f_ex_an_ar_oldlib.lock + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + fi + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ + 'stat=$?; rm -f "$lockfile"; exit $stat' + if test "$lock_old_archive_extraction" = yes; then + $opt_dry_run || rm -f "$lockfile" + fi + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $opt_debug + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + + 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 + func_basename "$my_xlib" + my_xlib="$func_basename_result" + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir="$my_gentop/$my_xlib_u" + + func_mkdir_p "$my_xdir" + + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + 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 | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` + done + + func_extract_archives_result="$my_oldobjs" +} + + +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory in which it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=${1-no} + + $ECHO "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# 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. +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# 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 variables: + generated_by_libtool_version='$macro_version' + 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 + file=\"\$0\"" + + qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` + $ECHO "\ + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + ECHO=\"$qECHO\" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ which is used only on +# windows platforms, and (c) all begin with the string "--lt-" +# (application programs are unlikely to have options which match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's $0 value, followed by "$@". +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=\$0 + shift + for lt_opt + do + case \"\$lt_opt\" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` + test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. + lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` + cat \"\$lt_dump_D/\$lt_dump_F\" + exit 0 + ;; + --lt-*) + \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n \"\$lt_option_debug\"; then + echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" + lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from \$@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case \" \$* \" in + *\\ --lt-*) + for lt_wr_arg + do + case \$lt_wr_arg in + --lt-*) ;; + *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; + esac + shift + done ;; + esac + func_exec_program_core \${1+\"\$@\"} +} + + # Parse options + func_parse_lt_options \"\$0\" \${1+\"\$@\"} + + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"\$file\" | $SED '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 \"\$file\" | $SED '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 \"\$file\" | $SED 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $ECHO "\ + 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 "\ + + # 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 1 + fi + fi + + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $ECHO "\ + + if test -f \"\$progdir/\$program\"; then" + + # fixup the dll searchpath if we need to. + # + # Fix the DLL searchpath if we need to. Do this before prepending + # to shlibpath, because on Windows, both are PATH and uninstalled + # libraries must come first. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # 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 \"\$$shlibpath_var\" | $SED 's/::*\$//'\` + + export $shlibpath_var +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. + func_exec_program \${1+\"\$@\"} + 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 1 + fi +fi\ +" +} + + +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat <<EOF + +/* $cwrappersource - temporary wrapper executable for $objdir/$outputname + Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION + + The $output program cannot be directly executed until all the libtool + libraries that it depends on are installed. + + This wrapper executable should never be moved out of the build directory. + If it is, it will not operate correctly. +*/ +EOF + cat <<"EOF" +#ifdef _MSC_VER +# define _CRT_SECURE_NO_DEPRECATE 1 +#endif +#include <stdio.h> +#include <stdlib.h> +#ifdef _MSC_VER +# include <direct.h> +# include <process.h> +# include <io.h> +#else +# include <unistd.h> +# include <stdint.h> +# ifdef __CYGWIN__ +# include <io.h> +# endif +#endif +#include <malloc.h> +#include <stdarg.h> +#include <assert.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> + +/* declarations of non-ANSI functions */ +#if defined(__MINGW32__) +# ifdef __STRICT_ANSI__ +int _putenv (const char *); +# endif +#elif defined(__CYGWIN__) +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +/* #elif defined (other platforms) ... */ +#endif + +/* portability defines, excluding path handling macros */ +#if defined(_MSC_VER) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +# define S_IXUSR _S_IEXEC +# ifndef _INTPTR_T_DEFINED +# define _INTPTR_T_DEFINED +# define intptr_t int +# endif +#elif defined(__MINGW32__) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +#elif defined(__CYGWIN__) +# define HAVE_SETENV +# define FOPEN_WB "wb" +/* #elif defined (other platforms) ... */ +#endif + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +/* path handling portability macros */ +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_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 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +#if defined(LT_DEBUGWRAPPER) +static int lt_debug = 1; +#else +static int lt_debug = 0; +#endif + +const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_debugprintf (const char *file, int line, const char *fmt, ...); +void lt_fatal (const char *file, int line, const char *message, ...); +static const char *nonnull (const char *s); +static const char *nonempty (const char *s); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); +char **prepare_spawn (char **argv); +void lt_dump_script (FILE *f); +EOF + + cat <<EOF +volatile const char * MAGIC_EXE = "$magic_exe"; +const char * LIB_PATH_VARNAME = "$shlibpath_var"; +EOF + + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + func_to_host_path "$temp_rpath" + cat <<EOF +const char * LIB_PATH_VALUE = "$func_to_host_path_result"; +EOF + else + cat <<"EOF" +const char * LIB_PATH_VALUE = ""; +EOF + fi + + if test -n "$dllsearchpath"; then + func_to_host_path "$dllsearchpath:" + cat <<EOF +const char * EXE_PATH_VARNAME = "PATH"; +const char * EXE_PATH_VALUE = "$func_to_host_path_result"; +EOF + else + cat <<"EOF" +const char * EXE_PATH_VARNAME = ""; +const char * EXE_PATH_VALUE = ""; +EOF + fi + + if test "$fast_install" = yes; then + cat <<EOF +const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */ +EOF + else + cat <<EOF +const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */ +EOF + fi + + + cat <<"EOF" + +#define LTWRAPPER_OPTION_PREFIX "--lt-" + +static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX; +static const char *dumpscript_opt = LTWRAPPER_OPTION_PREFIX "dump-script"; +static const char *debug_opt = LTWRAPPER_OPTION_PREFIX "debug"; + +int +main (int argc, char *argv[]) +{ + char **newargz; + int newargc; + char *tmp_pathspec; + char *actual_cwrapper_path; + char *actual_cwrapper_name; + char *target_name; + char *lt_argv_zero; + intptr_t rval = 127; + + int i; + + program_name = (char *) xstrdup (base_name (argv[0])); + newargz = XMALLOC (char *, argc + 1); + + /* very simple arg parsing; don't want to rely on getopt + * also, copy all non cwrapper options to newargz, except + * argz[0], which is handled differently + */ + newargc=0; + for (i = 1; i < argc; i++) + { + if (strcmp (argv[i], dumpscript_opt) == 0) + { +EOF + case "$host" in + *mingw* | *cygwin* ) + # make stdout use "unix" line endings + echo " setmode(1,_O_BINARY);" + ;; + esac + + cat <<"EOF" + lt_dump_script (stdout); + return 0; + } + if (strcmp (argv[i], debug_opt) == 0) + { + lt_debug = 1; + continue; + } + if (strcmp (argv[i], ltwrapper_option_prefix) == 0) + { + /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX + namespace, but it is not one of the ones we know about and + have already dealt with, above (inluding dump-script), then + report an error. Otherwise, targets might begin to believe + they are allowed to use options in the LTWRAPPER_OPTION_PREFIX + namespace. The first time any user complains about this, we'll + need to make LTWRAPPER_OPTION_PREFIX a configure-time option + or a configure.ac-settable value. + */ + lt_fatal (__FILE__, __LINE__, + "unrecognized %s option: '%s'", + ltwrapper_option_prefix, argv[i]); + } + /* otherwise ... */ + newargz[++newargc] = xstrdup (argv[i]); + } + newargz[++newargc] = NULL; + +EOF + cat <<EOF + /* The GNU banner must be the first non-error debug message */ + lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\n"); +EOF + cat <<"EOF" + lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]); + lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name); + + tmp_pathspec = find_executable (argv[0]); + if (tmp_pathspec == NULL) + lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]); + lt_debugprintf (__FILE__, __LINE__, + "(main) found exe (before symlink chase) at: %s\n", + tmp_pathspec); + + actual_cwrapper_path = chase_symlinks (tmp_pathspec); + lt_debugprintf (__FILE__, __LINE__, + "(main) found exe (after symlink chase) at: %s\n", + actual_cwrapper_path); + XFREE (tmp_pathspec); + + actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path)); + strendzap (actual_cwrapper_path, actual_cwrapper_name); + + /* wrapper name transforms */ + strendzap (actual_cwrapper_name, ".exe"); + tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1); + XFREE (actual_cwrapper_name); + actual_cwrapper_name = tmp_pathspec; + tmp_pathspec = 0; + + /* target_name transforms -- use actual target program name; might have lt- prefix */ + target_name = xstrdup (base_name (TARGET_PROGRAM_NAME)); + strendzap (target_name, ".exe"); + tmp_pathspec = lt_extend_str (target_name, ".exe", 1); + XFREE (target_name); + target_name = tmp_pathspec; + tmp_pathspec = 0; + + lt_debugprintf (__FILE__, __LINE__, + "(main) libtool target name: %s\n", + target_name); +EOF + + cat <<EOF + newargz[0] = + XMALLOC (char, (strlen (actual_cwrapper_path) + + strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1)); + strcpy (newargz[0], actual_cwrapper_path); + strcat (newargz[0], "$objdir"); + strcat (newargz[0], "/"); +EOF + + cat <<"EOF" + /* stop here, and copy so we don't have to do this twice */ + tmp_pathspec = xstrdup (newargz[0]); + + /* do NOT want the lt- prefix here, so use actual_cwrapper_name */ + strcat (newargz[0], actual_cwrapper_name); + + /* DO want the lt- prefix here if it exists, so use target_name */ + lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1); + XFREE (tmp_pathspec); + tmp_pathspec = NULL; +EOF + + case $host_os in + mingw*) + cat <<"EOF" + { + char* p; + while ((p = strchr (newargz[0], '\\')) != NULL) + { + *p = '/'; + } + while ((p = strchr (lt_argv_zero, '\\')) != NULL) + { + *p = '/'; + } + } +EOF + ;; + esac + + cat <<"EOF" + XFREE (target_name); + XFREE (actual_cwrapper_path); + XFREE (actual_cwrapper_name); + + lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */ + lt_setenv ("DUALCASE", "1"); /* for MSK sh */ + /* Update the DLL searchpath. EXE_PATH_VALUE ($dllsearchpath) must + be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath) + because on Windows, both *_VARNAMEs are PATH but uninstalled + libraries must come first. */ + lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE); + lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE); + + lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n", + nonnull (lt_argv_zero)); + for (i = 0; i < newargc; i++) + { + lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n", + i, nonnull (newargz[i])); + } + +EOF + + case $host_os in + mingw*) + cat <<"EOF" + /* execv doesn't actually work on mingw as expected on unix */ + newargz = prepare_spawn (newargz); + rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz); + if (rval == -1) + { + /* failed to start process */ + lt_debugprintf (__FILE__, __LINE__, + "(main) failed to launch target \"%s\": %s\n", + lt_argv_zero, nonnull (strerror (errno))); + return 127; + } + return rval; +EOF + ;; + *) + cat <<"EOF" + execv (lt_argv_zero, newargz); + return rval; /* =127, but avoids unused variable warning */ +EOF + ;; + esac + + cat <<"EOF" +} + +void * +xmalloc (size_t num) +{ + void *p = (void *) malloc (num); + if (!p) + lt_fatal (__FILE__, __LINE__, "memory exhausted"); + + return p; +} + +char * +xstrdup (const char *string) +{ + return string ? strcpy ((char *) xmalloc (strlen (string) + 1), + string) : NULL; +} + +const char * +base_name (const char *name) +{ + const char *base; + +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + /* Skip over the disk name in MSDOS pathnames. */ + if (isalpha ((unsigned char) name[0]) && name[1] == ':') + name += 2; +#endif + + for (base = name; *name; name++) + if (IS_DIR_SEPARATOR (*name)) + base = name + 1; + return base; +} + +int +check_executable (const char *path) +{ + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if ((stat (path, &st) >= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} + +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + int tmp_len; + char *concat_name; + + lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", + nonempty (wrapper)); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = q - p; + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} + +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + lt_debugprintf (__FILE__, __LINE__, + "checking path component for symlinks: %s\n", + tmp_pathspec); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } + + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + lt_fatal (__FILE__, __LINE__, + "error accessing file \"%s\": %s", + tmp_pathspec, nonnull (strerror (errno))); + } + } + XFREE (tmp_pathspec); + + if (!has_symlinks) + { + return xstrdup (pathspec); + } + + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal (__FILE__, __LINE__, + "could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} + +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; +} + +void +lt_debugprintf (const char *file, int line, const char *fmt, ...) +{ + va_list args; + if (lt_debug) + { + (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); + } +} + +static void +lt_error_core (int exit_status, const char *file, + int line, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *file, int line, const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); + va_end (ap); +} + +static const char * +nonnull (const char *s) +{ + return s ? s : "(null)"; +} + +static const char * +nonempty (const char *s) +{ + return (s && !*s) ? "(empty)" : nonnull (s); +} + +void +lt_setenv (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_setenv) setting '%s' to '%s'\n", + nonnull (name), nonnull (value)); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + int len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} + +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + int orig_value_len = strlen (orig_value); + int add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} + +void +lt_update_exe_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + int len = strlen (new_value); + while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[len-1] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +void +lt_update_lib_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +EOF + case $host_os in + mingw*) + cat <<"EOF" + +/* Prepares an argument vector before calling spawn(). + Note that spawn() does not by itself call the command interpreter + (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : + ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&v); + v.dwPlatformId == VER_PLATFORM_WIN32_NT; + }) ? "cmd.exe" : "command.com"). + Instead it simply concatenates the arguments, separated by ' ', and calls + CreateProcess(). We must quote the arguments since Win32 CreateProcess() + interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a + special way: + - Space and tab are interpreted as delimiters. They are not treated as + delimiters if they are surrounded by double quotes: "...". + - Unescaped double quotes are removed from the input. Their only effect is + that within double quotes, space and tab are treated like normal + characters. + - Backslashes not followed by double quotes are not special. + - But 2*n+1 backslashes followed by a double quote become + n backslashes followed by a double quote (n >= 0): + \" -> " + \\\" -> \" + \\\\\" -> \\" + */ +#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +char ** +prepare_spawn (char **argv) +{ + size_t argc; + char **new_argv; + size_t i; + + /* Count number of arguments. */ + for (argc = 0; argv[argc] != NULL; argc++) + ; + + /* Allocate new argument vector. */ + new_argv = XMALLOC (char *, argc + 1); + + /* Put quoted arguments into the new argument vector. */ + for (i = 0; i < argc; i++) + { + const char *string = argv[i]; + + if (string[0] == '\0') + new_argv[i] = xstrdup ("\"\""); + else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) + { + int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); + size_t length; + unsigned int backslashes; + const char *s; + char *quoted_string; + char *p; + + length = 0; + backslashes = 0; + if (quote_around) + length++; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + length += backslashes + 1; + length++; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + + quoted_string = XMALLOC (char, length + 1); + + p = quoted_string; + backslashes = 0; + if (quote_around) + *p++ = '"'; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + { + unsigned int j; + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + *p++ = c; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + unsigned int j; + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p = '\0'; + + new_argv[i] = quoted_string; + } + else + new_argv[i] = (char *) string; + } + new_argv[argc] = NULL; + + return new_argv; +} +EOF + ;; + esac + + cat <<"EOF" +void lt_dump_script (FILE* f) +{ +EOF + func_emit_wrapper yes | + $SED -n -e ' +s/^\(.\{79\}\)\(..*\)/\1\ +\2/ +h +s/\([\\"]\)/\\\1/g +s/$/\\n/ +s/\([^\n]*\).*/ fputs ("\1", f);/p +g +D' + cat <<"EOF" +} +EOF +} +# end: func_emit_cwrapperexe_src + +# func_win32_import_lib_p ARG +# True if ARG is an import lib, as indicated by $file_magic_cmd +func_win32_import_lib_p () +{ + $opt_debug + case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in + *import*) : ;; + *) false ;; + esac +} + +# func_mode_link arg... +func_mode_link () +{ + $opt_debug + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # which system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll which has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= + + avoid_version=no + bindir= + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=no + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module="${wl}-single_module" + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_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 + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + libtool_args+=" $func_quote_for_eval_result" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + compile_command+=" @OUTPUT@" + finalize_command+=" @OUTPUT@" + ;; + esac + + case $prev in + bindir) + bindir="$arg" + prev= + continue + ;; + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + compile_command+=" @SYMFILE@" + 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+=" $arg" + else + dlprefiles+=" $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + test -f "$arg" \ + || func_fatal_error "symbol file \`$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) deplibs+=" $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# moreargs+=" $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + 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+=" $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+=" $pic_object" + prev= + fi + + # A PIC object. + 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_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + non_pic_objects+=" $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + libobjs+=" $pic_object" + non_pic_objects+=" $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file \`$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) rpath+=" $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) xrpath+=" $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + weak) + weak_libs+=" $arg" + prev= + continue + ;; + xcclinker) + linker_flags+=" $qarg" + compiler_flags+=" $qarg" + prev= + compile_command+=" $qarg" + finalize_command+=" $qarg" + continue + ;; + xcompiler) + compiler_flags+=" $qarg" + prev= + compile_command+=" $qarg" + finalize_command+=" $qarg" + continue + ;; + xlinker) + linker_flags+=" $qarg" + compiler_flags+=" $wl$qarg" + prev= + compile_command+=" $wl$qarg" + finalize_command+=" $wl$qarg" + 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 + # See comment for -static flag below, for more details. + compile_command+=" $link_static_flag" + finalize_command+=" $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "\`-allow-undefined' must not be used because it is the default" + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -bindir) + prev=bindir + 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 + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework) + prev=framework + 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+=" $arg" + finalize_command+=" $arg" + ;; + esac + continue + ;; + + -L*) + func_stripname "-L" '' "$arg" + if test -z "$func_stripname_result"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between \`-L' and \`$1'" + else + func_fatal_error "need path for \`-L' option" + fi + fi + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of \`$dir'" + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "* | *" $arg "*) + # Will only happen for absolute or sysroot arguments + ;; + *) + # Preserve sysroot, but never include relative directories + case $dir in + [\\/]* | [A-Za-z]:[\\/]* | =*) deplibs+=" $arg" ;; + *) deplibs+=" -L$dir" ;; + esac + lib_search_path+=" $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) dllsearchpath+=":$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) dllsearchpath+=":$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # 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+=" System.ltframework" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test "X$arg" = "X-lc" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test "X$arg" = "X-lc" && continue + ;; + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + deplibs+=" $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + # Darwin uses the -arch flag to determine output architecture. + -model|-arch|-isysroot|--sysroot) + compiler_flags+=" $arg" + compile_command+=" $arg" + finalize_command+=" $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + compiler_flags+=" $arg" + compile_command+=" $arg" + finalize_command+=" $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) new_inherited_linker_flags+=" $arg" ;; + esac + continue + ;; + + -multi_module) + single_module="${wl}-multi_module" + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "\`-no-install' is ignored for $host" + func_warning "assuming \`-no-fast-install' instead" + 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*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + =*) + func_stripname '=' '' "$dir" + dir=$lt_sysroot$func_stripname_result + ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) xrpath+=" $dir" ;; + esac + continue + ;; + + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -static | -static-libtool-libs) + # 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 + ;; + + -weak) + prev=weak + continue + ;; + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + arg+=" $func_quote_for_eval_result" + compiler_flags+=" $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + arg+=" $wl$func_quote_for_eval_result" + compiler_flags+=" $wl$func_quote_for_eval_result" + linker_flags+=" $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + # Flags to be passed through unchanged, with rationale: + # -64, -mips[0-9] enable 64-bit mode for the SGI compiler + # -r[0-9][0-9]* specify processor for the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler + # +DA*, +DD* enable 64-bit mode for the HP compiler + # -q* compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* architecture-specific flags for GCC + # -F/path path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* profiling flags for GCC + # @file GCC response files + # -tp=* Portland pgcc target processor selection + # --sysroot=* for sysroot support + # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ + -O*|-flto*|-fwhopr*|-fuse-linker-plugin) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + compile_command+=" $arg" + finalize_command+=" $arg" + compiler_flags+=" $arg" + continue + ;; + + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + *.$objext) + # A standard object. + objs+=" $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + 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+=" $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+=" $pic_object" + prev= + fi + + # A PIC object. + 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_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + non_pic_objects+=" $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + libobjs+=" $pic_object" + non_pic_objects+=" $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + deplibs+=" $arg" + old_deplibs+=" $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + func_resolve_sysroot "$arg" + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + dlfiles+=" $func_resolve_sysroot_result" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + dlprefiles+=" $func_resolve_sysroot_result" + prev= + else + deplibs+=" $func_resolve_sysroot_result" + 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. + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + compile_command+=" $arg" + finalize_command+=" $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the \`$prevarg' option requires an argument" + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + compile_command+=" $arg" + finalize_command+=" $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname="$func_basename_result" + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'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\" + + func_dirname "$output" "/" "" + output_objdir="$func_dirname_result$objdir" + func_to_tool_file "$output_objdir/" + tool_output_objdir=$func_to_tool_file_result + # Create the object directory. + func_mkdir_p "$output_objdir" + + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + 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 $opt_preserve_dup_deps ; then + case "$libs " in + *" $deplib "*) specialdeplibs+=" $deplib" ;; + esac + fi + 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 $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) specialdeplibs+=" $pre_post_deps" ;; + esac + 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 dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" + ;; + 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 + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test "$linkmode,$pass" = "lib,link"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs="$tmp_deplibs" + fi + + 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 "$linkmode,$pass" = "lib,dlpreopen"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + func_resolve_sysroot "$lib" + case $lib in + *.la) func_source "$func_resolve_sysroot_result" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + func_basename "$deplib" + deplib_base=$func_basename_result + case " $weak_libs " in + *" $deplib_base "*) ;; + *) deplibs+=" $deplib" ;; + esac + done + done + libs="$dlprefiles" + 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 \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + compiler_flags+=" $deplib" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) new_inherited_linker_flags+=" $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + func_warning "\`-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test "$linkmode" = lib; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; 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 func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + 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 + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + 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 + *.ltframework) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) new_inherited_linker_flags+=" $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + newlib_search_path+=" $func_resolve_sysroot_result" + ;; + 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 + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + newlib_search_path+=" $func_resolve_sysroot_result" + ;; + *) + func_warning "\`-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + func_stripname '-R' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) xrpath+=" $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) + func_resolve_sysroot "$deplib" + lib=$func_resolve_sysroot_result + ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + 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 use here." + else + echo + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + ;; + esac + 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+=" $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + newdlfiles+=" $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + + if test "$found" = yes || test -f "$lib"; then : + else + func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" + fi + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "\`$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) new_inherited_linker_flags+=" $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && dlfiles+=" $dlopen" + test -n "$dlpreopen" && 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 + func_fatal_error "cannot find name of link library for \`$lib'" + fi + # It is a libtool convenience library, so add in its objects. + convenience+=" $ladir/$objdir/$old_library" + old_convenience+=" $ladir/$objdir/$old_library" + elif test "$linkmode" != prog && test "$linkmode" != lib; then + func_fatal_error "\`$lib' is not a convenience library" + fi + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs+=" $deplib" ;; + esac + fi + tmp_libs+=" $deplib" + done + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + if test -n "$old_library" && + { test "$prefer_static_libs" = yes || + test "$prefer_static_libs,$installed" = "built,no"; }; then + linklib=$old_library + else + for l in $old_library $library_names; do + linklib="$l" + done + fi + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + func_fatal_error "cannot -dlopen a convenience library: \`$lib'" + 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+=" $lib $dependency_libs" + else + 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 + func_warning "cannot determine absolute directory name of \`$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir="$ladir" + fi + ;; + esac + func_basename "$lib" + laname="$func_basename_result" + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library \`$lib' was moved." + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$lt_sysroot$libdir" + absdir="$lt_sysroot$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + 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+=" $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + notinst_path+=" $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir" && test "$linkmode" = prog; then + func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" + fi + case "$host" in + # special handling for platforms with PE-DLLs. + *cygwin* | *mingw* | *cegcc* ) + # Linker will automatically link against shared library if both + # static and shared are present. Therefore, ensure we extract + # symbols from the import library if a shared library is present + # (otherwise, the dlopen module name will be incorrect). We do + # this by putting the import library name into $newdlprefiles. + # We recover the dlopen module name by 'saving' the la file + # name in a special purpose variable, and (later) extracting the + # dlname from the la file. + if test -n "$dlname"; then + func_tr_sh "$dir/$linklib" + eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" + newdlprefiles+=" $dir/$linklib" + else + newdlprefiles+=" $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + dlpreconveniencelibs+=" $dir/$old_library" + fi + ;; + * ) + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + newdlprefiles+=" $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + dlpreconveniencelibs+=" $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + newdlprefiles+=" $dir/$dlname" + else + newdlprefiles+=" $dir/$linklib" + fi + ;; + esac + 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+=" $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*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + newlib_search_path+=" $func_resolve_sysroot_result" + ;; + 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 $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs+=" $deplib" ;; + esac + fi + 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 "$prefer_static_libs,$installed" = "built,yes"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath:" in + *"$absdir:"*) ;; + *) temp_rpath+="$absdir:" ;; + 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+=" $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) 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 + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc*) + # No point in relinking DLLs because paths are not encoded + notinst_deplibs+=" $lib" + need_relink=no + ;; + *) + if test "$installed" = no; then + notinst_deplibs+=" $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule="" + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule="$dlpremoduletest" + break + fi + done + if test -z "$dlopenmodule" && 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+=" $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath+=" $libdir" ;; + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname="$1" + 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* | *cegcc*) + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + func_basename "$soroot" + soname="$func_basename_result" + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from \`$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for \`$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + 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 "$opt_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.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we can not + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null ; then + if test "X$dlopenmodule" != "X$lib"; 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 + elif test -n "$old_library"; then + 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 && + test "$hardcode_direct_absolute" = no; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$absdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + 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 + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_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+="$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$opt_mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; 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+="$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+=" -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 + 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*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) xrpath+=" $temp_xrpath";; + esac;; + *) temp_deplibs+=" $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + 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" + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result";; + *) func_resolve_sysroot "$deplib" ;; + esac + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $func_resolve_sysroot_result "*) + specialdeplibs+=" $func_resolve_sysroot_result" ;; + esac + fi + tmp_libs+=" $func_resolve_sysroot_result" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + path= + case $deplib in + -L*) path="$deplib" ;; + *.la) + func_resolve_sysroot "$deplib" + deplib=$func_resolve_sysroot_result + func_dirname "$deplib" "" "." + dir=$func_dirname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of \`$dir'" + absdir="$dir" + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + 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 "$absdir/$objdir/$depdepl" ; then + depdepl="$absdir/$objdir/$depdepl" + darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + compiler_flags+=" ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" + linker_flags+=" -dylib_file ${darwin_install_name}:${depdepl}" + path= + fi + fi + ;; + *) + path="-L$absdir/$objdir" + ;; + esac + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "\`$deplib' seems to be moved" + + path="-L$absdir" + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test "$pass" = link; then + if test "$linkmode" = "prog"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + 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+=" $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+=" $deplib" ;; + esac + ;; + *) 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+=" $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + fi + if test "$linkmode" = prog || test "$linkmode" = lib; then + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "\`-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "\`-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + objs+="$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test "$module" = no && \ + func_fatal_help "libtool library \`$output' must begin with \`lib'" + + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" + else + echo + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + libobjs+=" $objs" + fi + fi + + test "$dlself" != no && \ + func_warning "\`-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test "$#" -gt 1 && \ + func_warning "ignoring multiple \`-rpath's for a libtool library" + + install_libdir="$1" + + 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 + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "\`-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + shift + IFS="$save_ifs" + + test -n "$7" && \ + func_fatal_help "too many parameters to \`-version-info'" + + # 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="$1" + number_minor="$2" + number_revision="$3" + # + # 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 + # correct linux to gnu/linux during the next big refactor + darwin|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|qnx|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_minor" + lt_irix_increment=no + ;; + esac + ;; + no) + current="$1" + revision="$2" + age="$3" + ;; + 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]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT \`$current' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION \`$revision' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE \`$age' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE \`$age' is greater than the current interface number \`$current'" + func_fatal_error "\`$vinfo' is not valid version information" + 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 + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current" + ;; + + irix | nonstopux) + if test "X$lt_irix_increment" = "Xno"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + 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 + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) # correct to gnu/linux during the next big refactor + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + 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 + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + verstring+=":${current}.0" + ;; + + qnx) + major=".$current" + versuffix=".$current" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + + *) + func_fatal_configuration "unknown library version type \`$version_type'" + ;; + 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 + func_warning "undefined symbols not allowed in $host shared libraries" + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + + fi + + func_generate_dlsyms "$libname" "$libname" "yes" + libobjs+=" $symfileobj" + test "X$libobjs" = "X " && libobjs= + + if test "$opt_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 | *.gcno) + ;; + $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+=" $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + oldlibs+=" $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` + # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` + # dependency_libs=`$ECHO "$dependency_libs " | $SED "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 + func_replace_sysroot "$libdir" + temp_xrpath+=" -R$func_replace_sysroot_result" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) 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+=" $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+=" $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + deplibs+=" System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + 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. + $opt_dry_run || $RM conftest.c + cat > conftest.c <<EOF + int main() { return 0; } +EOF + $opt_dry_run || $RM conftest + if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then + ldd_output=`ldd conftest` + for i in $deplibs; do + case $i in + -l*) + func_stripname -l '' "$i" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $i "*) + newdeplibs+=" $i" + i="" + ;; + esac + fi + if test -n "$i" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + deplib_matches=`eval "\\$ECHO \"$library_names_spec\""` + set dummy $deplib_matches; shift + deplib_match=$1 + if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then + newdeplibs+=" $i" + else + droppeddeps=yes + echo + $ECHO "*** Warning: dynamic linker does not accept needed library $i." + 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 I believe you do not have" + echo "*** because a test_compile did reveal that the linker did not use it for" + echo "*** its dynamic dependency list that programs get resolved with at runtime." + fi + fi + ;; + *) + newdeplibs+=" $i" + ;; + esac + done + else + # Error occurred in the first compile. Let's try to salvage + # the situation: Compile a separate program for each library. + for i in $deplibs; do + case $i in + -l*) + func_stripname -l '' "$i" + name=$func_stripname_result + $opt_dry_run || $RM conftest + if $LTCC $LTCFLAGS -o conftest conftest.c $i; then + ldd_output=`ldd conftest` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $i "*) + newdeplibs+=" $i" + i="" + ;; + esac + fi + if test -n "$i" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + deplib_matches=`eval "\\$ECHO \"$library_names_spec\""` + set dummy $deplib_matches; shift + deplib_match=$1 + if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then + newdeplibs+=" $i" + else + droppeddeps=yes + echo + $ECHO "*** Warning: dynamic linker does not accept needed library $i." + 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 a test_compile did reveal that the linker did not use this one" + echo "*** as a dynamic dependency that programs can get resolved with at runtime." + fi + fi + else + droppeddeps=yes + echo + $ECHO "*** Warning! Library $i is needed by this library but I was not able to" + echo "*** make it link in! You will probably need to install it or some" + echo "*** library that it depends on before this library will be fully" + echo "*** functional. Installing it before continuing would be even better." + fi + ;; + *) + newdeplibs+=" $i" + ;; + esac + done + fi + ;; + file_magic*) + set dummy $deplibs_check_method; shift + file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + newdeplibs+=" $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + if test -n "$file_magic_glob"; then + libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob` + else + libnameglob=$libname + fi + test "$want_nocaseglob" = yes && nocaseglob=`shopt -p nocaseglob` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + if test "$want_nocaseglob" = yes; then + shopt -s nocaseglob + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + $nocaseglob + else + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + fi + 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 "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + 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 + ;; + *) + # Add a -L argument. + newdeplibs+=" $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + 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+=" $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 + ;; + *) + # Add a -L argument. + newdeplibs+=" $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; 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 " $tmp_deplibs" | $SED "s,$i,,"` + done + fi + case $tmp_deplibs in + *[!\ \ ]*) + 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 + ;; + esac + ;; + 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 with the System framework + newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + 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 + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + new_libs+=" -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs+=" $deplib" ;; + esac + ;; + *) new_libs+=" $deplib" ;; + esac + done + deplibs="$new_libs" + + # 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 + # Remove ${wl} instances when linking with ld. + # FIXME: should test the right _cmds variable. + case $archive_cmds in + *\$LD\ *) wl= ;; + esac + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$opt_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 + func_replace_sysroot "$libdir" + libdir=$func_replace_sysroot_result + 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_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + dep_rpath+=" $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) 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 "dep_rpath=\"$hardcode_libdir_flag_spec\"" + 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+="$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 "$opt_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 + shift + realname="$1" + 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" + linknames= + for link + do + linknames+=" $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= + + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols="$output_objdir/$libname.uexp" + delfiles+=" $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + if test "x`$SED 1q $export_symbols`" != xEXPORTS; then + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols="$export_symbols" + export_symbols= + always_export_symbols=yes + fi + fi + ;; + esac + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd1 in $cmds; do + IFS="$save_ifs" + # Take the normal branch if the nm_file_list_spec branch + # doesn't work or if tool conversion is not needed. + case $nm_file_list_spec~$to_tool_file_cmd in + *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) + try_normal_branch=yes + eval cmd=\"$cmd1\" + func_len " $cmd" + len=$func_len_result + ;; + *) + try_normal_branch=no + ;; + esac + if test "$try_normal_branch" = yes \ + && { test "$len" -lt "$max_cmd_len" \ + || test "$max_cmd_len" -le -1; } + then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + elif test -n "$nm_file_list_spec"; then + func_basename "$output" + output_la=$func_basename_result + save_libobjs=$libobjs + save_output=$output + output=${output_objdir}/${output_la}.nm + func_to_tool_file "$output" + libobjs=$nm_file_list_spec$func_to_tool_file_result + delfiles+=" $output" + func_verbose "creating $NM input file list: $output" + for obj in $save_libobjs; do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > "$output" + eval cmd=\"$cmd1\" + func_show_eval "$cmd" 'exit $?' + output=$save_output + libobjs=$save_libobjs + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + delfiles+=" $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + tmp_deplibs+=" $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test "$compiler_needs_object" = yes && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop="$output_objdir/${outputname}x" + generated+=" $gentop" + + func_extract_archives $gentop $convenience + libobjs+=" $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + linker_flags+=" $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$opt_mode" = relink; then + $opt_dry_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:" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. + + # 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 + func_basename "$output" + output_la=$func_basename_result + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 + + if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then + output=${output_objdir}/${output_la}.lnkscript + func_verbose "creating GNU ld script: $output" + echo 'INPUT (' > $output + for obj in $save_libobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + echo ')' >> $output + delfiles+=" $output" + func_to_tool_file "$output" + output=$func_to_tool_file_result + elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then + output=${output_objdir}/${output_la}.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test "$compiler_needs_object" = yes; then + firstobj="$1 " + shift + fi + for obj + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + delfiles+=" $output" + func_to_tool_file "$output" + output=$firstobj\"$file_list_spec$func_to_tool_file_result\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-${k}.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test "X$objlist" = X || + test "$len" -lt "$max_cmd_len"; then + 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. + reload_objs=$objlist + eval concat_cmds=\"$reload_cmds\" + else + # All subsequent reloadable object files will link in + # the last one created. + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-${k}.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-${k}.$objext + objlist=" $obj" + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + 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~ + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\${concat_cmds}$reload_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" + fi + delfiles+=" $output" + + else + output= + fi + + if ${skipped_export-false}; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + fi + + test -n "$save_libobjs" && + func_verbose "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" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + + if ${skipped_export-false}; then + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + delfiles+=" $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + fi + + 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\" + test "X$libobjs" = "X " && libobjs= + 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 + fi + + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + generated+=" $gentop" + + func_extract_archives $gentop $dlprefiles + libobjs+=" $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_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 "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for objects" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "\`-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object \`$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $opt_dry_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 and hope we can get by with + # turning comma into space.. + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` + else + gentop="$output_objdir/${obj}x" + generated+=" $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # If we're not building shared, we need to use non_pic_objs + test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + func_execute_cmds "$reload_cmds" 'exit $?' + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + func_show_eval '${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" + # $opt_dry_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" + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for programs" + + test -n "$release" && \ + func_warning "\`-release' is ignored for programs" + + test "$preload" = yes \ + && test "$dlopen_support" = unknown \ + && test "$dlopen_self" = unknown \ + && test "$dlopen_self_static" = unknown && \ + func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test "$tagname" = CXX ; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + compile_command+=" ${wl}-bind_at_load" + finalize_command+=" ${wl}-bind_at_load" + ;; + esac + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + new_libs+=" -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs+=" $deplib" ;; + esac + ;; + *) new_libs+=" $deplib" ;; + esac + done + compile_deplibs="$new_libs" + + + compile_command+=" $compile_deplibs" + 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+=" $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_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath+=" $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath+=" $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) dllsearchpath+=":$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) dllsearchpath+=":$testbindir";; + 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_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath+=" $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) 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 "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + fi + + func_generate_dlsyms "$outputname" "@PROGRAM@" "no" + + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi + + wrappers_required=yes + case $host in + *cegcc* | *mingw32ce*) + # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. + wrappers_required=no + ;; + *cygwin* | *mingw* ) + if test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + *) + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + esac + if test "$wrappers_required" = no; then + # Replace the output file specification. + compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.${objext}"; then + func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' + fi + + exit $exit_status + 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+="$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+="$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 "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + 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" + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "\`$output' will be relinked during installation" + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED '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 "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname + + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output_objdir/$outputname" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Now create the wrapper script. + func_verbose "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}\" || $lt_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 + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource="$output_path/$objdir/lt-$output_name.c" + cwrapper="$output_path/$output_name.exe" + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } + + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host" ; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 + + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + 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 $symfileobj" + 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" + if test "$preload" = yes && test -f "$symfileobj"; then + oldobjs+=" $symfileobj" + fi + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + generated+=" $gentop" + + func_extract_archives $gentop $addlibs + 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 + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + generated+=" $gentop" + + func_extract_archives $gentop $dlprefiles + oldobjs+=" $func_extract_archives_result" + fi + + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + echo "copying selected object files to avoid basename conflicts..." + gentop="$output_objdir/${outputname}x" + generated+=" $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase="$func_basename_result" + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + oldobjs+=" $gentop/$newobj" + ;; + *) oldobjs+=" $obj" ;; + esac + done + fi + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + elif test -n "$archiver_list_spec"; then + func_verbose "using command file archive linking..." + for obj in $oldobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > $output_objdir/$libname.libcmd + func_to_tool_file "$output_objdir/$libname.libcmd" + oldobjs=" $archiver_list_spec$func_to_tool_file_result" + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + objlist+=" $obj" + if test "$len" -lt "$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= + len=$len0 + 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 + func_execute_cmds "$cmds" 'exit $?' + done + + test -n "$generated" && \ + func_show_eval "${RM}r$generated" + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + func_verbose "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}\" || $lt_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 + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; 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 "$relink_command" | $SED "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + + # Only create the output if not a dry run. + $opt_dry_run || { + 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) + func_basename "$deplib" + name="$func_basename_result" + func_resolve_sysroot "$deplib" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + newdependency_libs+=" ${lt_sysroot:+=}$libdir/$name" + ;; + -L*) + func_stripname -L '' "$deplib" + func_replace_sysroot "$func_stripname_result" + newdependency_libs+=" -L$func_replace_sysroot_result" + ;; + -R*) + func_stripname -R '' "$deplib" + func_replace_sysroot "$func_stripname_result" + newdependency_libs+=" -R$func_replace_sysroot_result" + ;; + *) newdependency_libs+=" $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + newdlfiles+=" ${lt_sysroot:+=}$libdir/$name" + ;; + *) newdlfiles+=" $lib" ;; + esac + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + newdlprefiles+=" ${lt_sysroot:+=}$libdir/$name" + ;; + esac + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlfiles+=" $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlprefiles+=" $abs" + done + dlprefiles="$newdlprefiles" + fi + $RM $output + # place dlname in correct position for cygwin + # In fact, it would be nice if we could use this code for all target + # systems that can't hard-code library paths into their executables + # and that have no shared library path variable independent of PATH, + # but it turns out we can't easily determine that from inspecting + # libtool variables, so we have to hard-code the OSs to which it + # applies here; at the moment, that means platforms that use the PE + # object format with DLL files. See the long comment at the top of + # tests/bindir.at for full details. + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) + # If a -bindir argument was supplied, place the dll there. + if test "x$bindir" != x ; + then + func_relative_path "$install_libdir" "$bindir" + tdlname=$func_relative_path_result$dlname + else + # Otherwise fall back on heuristic. + tdlname=../bin/$dlname + fi + ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# 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' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Names of additional weak libraries provided by this library +weak_library_names='$weak_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 + } + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} + +{ test "$opt_mode" = link || test "$opt_mode" = relink; } && + func_mode_link ${1+"$@"} + + +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $opt_debug + 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+=" $arg"; rmforce=yes ;; + -*) RM+=" $arg" ;; + *) files+=" $arg" ;; + esac + done + + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" + + rmdirs= + + for file in $files; do + func_dirname "$file" "" "." + dir="$func_dirname_result" + if test "X$dir" = X.; then + odir="$objdir" + else + odir="$dir/$objdir" + fi + func_basename "$file" + name="$func_basename_result" + test "$opt_mode" = uninstall && odir="$dir" + + # Remember odir for removal later, being careful to avoid duplicates + if test "$opt_mode" = clean; then + case " $rmdirs " in + *" $odir "*) ;; + *) rmdirs+=" $odir" ;; + 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 func_lalib_p "$file"; then + func_source $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + rmfiles+=" $odir/$n" + done + test -n "$old_library" && rmfiles+=" $odir/$old_library" + + case "$opt_mode" in + clean) + case " $library_names " in + *" $dlname "*) ;; + *) test -n "$dlname" && rmfiles+=" $odir/$dlname" ;; + esac + test -n "$libdir" && rmfiles+=" $odir/$name $odir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if func_lalib_p "$file"; then + + # Read the .lo file + func_source $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" && + test "$pic_object" != none; then + 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+=" $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$opt_mode" = clean ; then + noexename=$name + case $file in + *.exe) + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result + # $file with .exe has already been added to rmfiles, + # add $file without .exe + rmfiles+=" $file" + ;; + esac + # Do a test to see if this is a libtool program. + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + rmfiles+=" $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + rmfiles+=" $odir/$name $odir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + rmfiles+=" $odir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + rmfiles+=" $odir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + func_show_eval "$RM $rmfiles" 'exit_status=1' + done + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + func_show_eval "rmdir $dir >/dev/null 2>&1" + fi + done + + exit $exit_status +} + +{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && + func_mode_uninstall ${1+"$@"} + +test -z "$opt_mode" && { + help="$generic_help" + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode \`$opt_mode'" + +if test -n "$exec_cmd"; then + eval exec "$exec_cmd" + exit $EXIT_FAILURE +fi + +exit $exit_status + + +# 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: +# vi:sw=2 + diff --git a/ltmain.sh b/ltmain.sh new file mode 100755 index 0000000000000000000000000000000000000000..63ae69dc6fecaf83c52fba2ad334f4b1369fb1cd --- /dev/null +++ b/ltmain.sh @@ -0,0 +1,9655 @@ + +# libtool (GNU libtool) 2.4.2 +# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996 + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, +# 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool 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. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, +# or obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Usage: $progname [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 +# --mode=MODE use operation mode MODE +# --preserve-dup-deps don't remove duplicate dependency libraries +# --quiet, --silent don't print informational messages +# --no-quiet, --no-silent +# print informational messages (default) +# --no-warn don't display warning messages +# --tag=TAG use configuration variables from tag TAG +# -v, --verbose print more informational messages than default +# --no-verbose don't print the extra informational messages +# --version print version information +# -h, --help, --help-all print short, long, or detailed help message +# +# 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. When passed as first option, +# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. +# Try `$progname --help --mode=MODE' for a more detailed description of MODE. +# +# When reporting a bug, please describe a test case to reproduce it and +# include the following information: +# +# host-triplet: $host +# shell: $SHELL +# compiler: $LTCC +# compiler flags: $LTCFLAGS +# linker: $LD (gnu? $with_gnu_ld) +# $progname: (GNU libtool) 2.4.2 +# automake: $automake_version +# autoconf: $autoconf_version +# +# Report bugs to <bug-libtool@gnu.org>. +# GNU libtool home page: <http://www.gnu.org/software/libtool/>. +# General help using GNU software: <http://www.gnu.org/gethelp/>. + +PROGRAM=libtool +PACKAGE=libtool +VERSION=2.4.2 +TIMESTAMP="" +package_revision=1.3337 + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# NLS nuisances: We save the old values to restore during execute mode. +lt_user_locale= +lt_safe_locale= +for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test \"\${$lt_var+set}\" = set; then + save_$lt_var=\$$lt_var + $lt_var=C + export $lt_var + lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" + lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" + fi" +done +LC_ALL=C +LANGUAGE=C +export LANGUAGE LC_ALL + +$lt_unset CDPATH + + +# 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" + + + +: ${CP="cp -f"} +test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} +: ${Xsed="$SED -e 1s/^X//"} + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +exit_status=$EXIT_SUCCESS + +# Make sure IFS has a sensible default +lt_nl=' +' +IFS=" $lt_nl" + +dirname="s,/[^/]*$,," +basename="s,^.*/,," + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi +} # func_dirname may be replaced by extended shell implementation + + +# func_basename file +func_basename () +{ + func_basename_result=`$ECHO "${1}" | $SED "$basename"` +} # func_basename may be replaced by extended shell implementation + + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi + func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` +} # func_dirname_and_basename may be replaced by extended shell implementation + + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# func_strip_suffix prefix name +func_stripname () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname may be replaced by extended shell implementation + + +# These SED scripts presuppose an absolute path with a trailing slash. +pathcar='s,^/\([^/]*\).*$,\1,' +pathcdr='s,^/[^/]*,,' +removedotparts=':dotsl + s@/\./@/@g + t dotsl + s,/\.$,/,' +collapseslashes='s@/\{1,\}@/@g' +finalslash='s,/*$,/,' + +# func_normal_abspath PATH +# Remove doubled-up and trailing slashes, "." path components, +# and cancel out any ".." path components in PATH after making +# it an absolute path. +# value returned in "$func_normal_abspath_result" +func_normal_abspath () +{ + # Start from root dir and reassemble the path. + func_normal_abspath_result= + func_normal_abspath_tpath=$1 + func_normal_abspath_altnamespace= + case $func_normal_abspath_tpath in + "") + # Empty path, that just means $cwd. + func_stripname '' '/' "`pwd`" + func_normal_abspath_result=$func_stripname_result + return + ;; + # The next three entries are used to spot a run of precisely + # two leading slashes without using negated character classes; + # we take advantage of case's first-match behaviour. + ///*) + # Unusual form of absolute path, do nothing. + ;; + //*) + # Not necessarily an ordinary path; POSIX reserves leading '//' + # and for example Cygwin uses it to access remote file shares + # over CIFS/SMB, so we conserve a leading double slash if found. + func_normal_abspath_altnamespace=/ + ;; + /*) + # Absolute path, do nothing. + ;; + *) + # Relative path, prepend $cwd. + func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath + ;; + esac + # Cancel out all the simple stuff to save iterations. We also want + # the path to end with a slash for ease of parsing, so make sure + # there is one (and only one) here. + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` + while :; do + # Processed it all yet? + if test "$func_normal_abspath_tpath" = / ; then + # If we ascended to the root using ".." the result may be empty now. + if test -z "$func_normal_abspath_result" ; then + func_normal_abspath_result=/ + fi + break + fi + func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcar"` + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcdr"` + # Figure out what to do with it + case $func_normal_abspath_tcomponent in + "") + # Trailing empty path component, ignore it. + ;; + ..) + # Parent dir; strip last assembled component from result. + func_dirname "$func_normal_abspath_result" + func_normal_abspath_result=$func_dirname_result + ;; + *) + # Actual path component, append it. + func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent + ;; + esac + done + # Restore leading double-slash if one was found on entry. + func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result +} + +# func_relative_path SRCDIR DSTDIR +# generates a relative path from SRCDIR to DSTDIR, with a trailing +# slash if non-empty, suitable for immediately appending a filename +# without needing to append a separator. +# value returned in "$func_relative_path_result" +func_relative_path () +{ + func_relative_path_result= + func_normal_abspath "$1" + func_relative_path_tlibdir=$func_normal_abspath_result + func_normal_abspath "$2" + func_relative_path_tbindir=$func_normal_abspath_result + + # Ascend the tree starting from libdir + while :; do + # check if we have found a prefix of bindir + case $func_relative_path_tbindir in + $func_relative_path_tlibdir) + # found an exact match + func_relative_path_tcancelled= + break + ;; + $func_relative_path_tlibdir*) + # found a matching prefix + func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" + func_relative_path_tcancelled=$func_stripname_result + if test -z "$func_relative_path_result"; then + func_relative_path_result=. + fi + break + ;; + *) + func_dirname $func_relative_path_tlibdir + func_relative_path_tlibdir=${func_dirname_result} + if test "x$func_relative_path_tlibdir" = x ; then + # Have to descend all the way to the root! + func_relative_path_result=../$func_relative_path_result + func_relative_path_tcancelled=$func_relative_path_tbindir + break + fi + func_relative_path_result=../$func_relative_path_result + ;; + esac + done + + # Now calculate path; take care to avoid doubling-up slashes. + func_stripname '' '/' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + func_stripname '/' '/' "$func_relative_path_tcancelled" + if test "x$func_stripname_result" != x ; then + func_relative_path_result=${func_relative_path_result}/${func_stripname_result} + fi + + # Normalisation. If bindir is libdir, return empty string, + # else relative path ending with a slash; either way, target + # file name can be directly appended. + if test ! -z "$func_relative_path_result"; then + func_stripname './' '' "$func_relative_path_result/" + func_relative_path_result=$func_stripname_result + fi +} + +# The name of this program: +func_dirname_and_basename "$progpath" +progname=$func_basename_result + +# Make sure we have an absolute path for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=$func_dirname_result + progdir=`cd "$progdir" && pwd` + progpath="$progdir/$progname" + ;; + *) + save_IFS="$IFS" + IFS=${PATH_SEPARATOR-:} + for progdir in $PATH; do + IFS="$save_IFS" + test -x "$progdir/$progname" && break + done + IFS="$save_IFS" + test -n "$progdir" || progdir=`pwd` + progpath="$progdir/$progname" + ;; +esac + +# 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='s/\([`"$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution that turns a string into a regex matching for the +# string literally. +sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' + +# Sed substitution that converts a w32 file name or path +# which contains forward slashes, into one that contains +# (escaped) backslashes. A very naive implementation. +lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + +# Re-`\' parameter expansions in output of double_quote_subst that were +# `\'-ed in input to the same. If an odd number of `\' preceded a '$' +# in input to double_quote_subst, that '$' was protected from expansion. +# Since each input `\' is now two `\'s, look for any number of runs of +# four `\'s followed by two `\'s and then a '$'. `\' that '$'. +bs='\\' +bs2='\\\\' +bs4='\\\\\\\\' +dollar='\$' +sed_double_backslash="\ + s/$bs4/&\\ +/g + s/^$bs2$dollar/$bs&/ + s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g + s/\n//g" + +# Standard options: +opt_dry_run=false +opt_help=false +opt_quiet=false +opt_verbose=false +opt_warning=: + +# func_echo arg... +# Echo program name prefixed message, along with the current mode +# name if it has been set yet. +func_echo () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }$*" +} + +# func_verbose arg... +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $opt_verbose && func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +# func_error arg... +# Echo program name prefixed message to standard error. +func_error () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 +} + +# func_warning arg... +# Echo program name prefixed warning message to standard error. +func_warning () +{ + $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 + + # bash bug again: + : +} + +# func_fatal_error arg... +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + +# func_fatal_help arg... +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + func_error ${1+"$@"} + func_fatal_error "$help" +} +help="Try \`$progname --help' for more information." ## default + + +# func_grep expression filename +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_mkdir_p directory-path +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + my_directory_path="$1" + my_dir_list= + + if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then + + # Protect directory names starting with `-' + case $my_directory_path in + -*) my_directory_path="./$my_directory_path" ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$my_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + my_dir_list="$my_directory_path:$my_dir_list" + + # If the last portion added has no slash in it, the list is done + case $my_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` + done + my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` + + save_mkdir_p_IFS="$IFS"; IFS=':' + for my_dir in $my_dir_list; do + IFS="$save_mkdir_p_IFS" + # mkdir can fail with a `File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$my_dir" 2>/dev/null || : + done + IFS="$save_mkdir_p_IFS" + + # Bail out if we (or some other process) failed to create a directory. + test -d "$my_directory_path" || \ + func_fatal_error "Failed to create \`$1'" + fi +} + + +# func_mktempdir [string] +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, STRING is the basename for that directory. +func_mktempdir () +{ + my_template="${TMPDIR-/tmp}/${1-$progname}" + + if test "$opt_dry_run" = ":"; then + # Return a directory name, but don't create it in dry-run mode + my_tmpdir="${my_template}-$$" + else + + # If mktemp works, use that first and foremost + my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` + + if test ! -d "$my_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" + + save_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$my_tmpdir" + umask $save_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$my_tmpdir" || \ + func_fatal_error "cannot create temporary directory \`$my_tmpdir'" + fi + + $ECHO "$my_tmpdir" +} + + +# func_quote_for_eval arg +# Aesthetically quote ARG to be evaled later. +# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT +# is double-quoted, suitable for a subsequent eval, whereas +# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters +# which are still active within double quotes backslashified. +func_quote_for_eval () +{ + case $1 in + *[\\\`\"\$]*) + func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; + *) + func_quote_for_eval_unquoted_result="$1" ;; + esac + + case $func_quote_for_eval_unquoted_result in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and and variable + # expansion for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" + ;; + *) + func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" + esac +} + + +# func_quote_for_expand arg +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + case $1 in + *[\\\`\"]*) + my_arg=`$ECHO "$1" | $SED \ + -e "$double_quote_subst" -e "$sed_double_backslash"` ;; + *) + my_arg="$1" ;; + esac + + case $my_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + my_arg="\"$my_arg\"" + ;; + esac + + func_quote_for_expand_result="$my_arg" +} + + +# func_show_eval cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$my_cmd" + my_status=$? + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + + +# func_show_eval_locale cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$lt_user_locale + $my_cmd" + my_status=$? + eval "$lt_safe_locale" + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + +# func_tr_sh +# Turn $1 into a string suitable for a shell variable name. +# Result is stored in $func_tr_sh_result. All characters +# not in the set a-zA-Z0-9_ are replaced with '_'. Further, +# if $1 begins with a digit, a '_' is prepended as well. +func_tr_sh () +{ + case $1 in + [0-9]* | *[!a-zA-Z0-9_]*) + func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` + ;; + * ) + func_tr_sh_result=$1 + ;; + esac +} + + +# func_version +# Echo version message to standard output and exit. +func_version () +{ + $opt_debug + + $SED -n '/(C)/!b go + :more + /\./!{ + N + s/\n# / / + b more + } + :go + /^# '$PROGRAM' (GNU /,/# warranty; / { + s/^# // + s/^# *$// + s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ + p + }' < "$progpath" + exit $? +} + +# func_usage +# Echo short help message to standard output and exit. +func_usage () +{ + $opt_debug + + $SED -n '/^# Usage:/,/^# *.*--help/ { + s/^# // + s/^# *$// + s/\$progname/'$progname'/ + p + }' < "$progpath" + echo + $ECHO "run \`$progname --help | more' for full usage" + exit $? +} + +# func_help [NOEXIT] +# Echo long help message to standard output and exit, +# unless 'noexit' is passed as argument. +func_help () +{ + $opt_debug + + $SED -n '/^# Usage:/,/# Report bugs to/ { + :print + s/^# // + s/^# *$// + s*\$progname*'$progname'* + s*\$host*'"$host"'* + s*\$SHELL*'"$SHELL"'* + s*\$LTCC*'"$LTCC"'* + s*\$LTCFLAGS*'"$LTCFLAGS"'* + s*\$LD*'"$LD"'* + s/\$with_gnu_ld/'"$with_gnu_ld"'/ + s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/ + s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/ + p + d + } + /^# .* home page:/b print + /^# General help using/b print + ' < "$progpath" + ret=$? + if test -z "$1"; then + exit $ret + fi +} + +# func_missing_arg argname +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + $opt_debug + + func_error "missing argument for $1." + exit_cmd=exit +} + + +# func_split_short_opt shortopt +# Set func_split_short_opt_name and func_split_short_opt_arg shell +# variables after splitting SHORTOPT after the 2nd character. +func_split_short_opt () +{ + my_sed_short_opt='1s/^\(..\).*$/\1/;q' + my_sed_short_rest='1s/^..\(.*\)$/\1/;q' + + func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"` + func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"` +} # func_split_short_opt may be replaced by extended shell implementation + + +# func_split_long_opt longopt +# Set func_split_long_opt_name and func_split_long_opt_arg shell +# variables after splitting LONGOPT at the `=' sign. +func_split_long_opt () +{ + my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q' + my_sed_long_arg='1s/^--[^=]*=//' + + func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"` + func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"` +} # func_split_long_opt may be replaced by extended shell implementation + +exit_cmd=: + + + + + +magic="%%%MAGIC variable%%%" +magic_exe="%%%MAGIC EXE variable%%%" + +# Global variables. +nonopt= +preserve_args= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" +extracted_archives= +extracted_serial=0 + +# 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= + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "${1}=\$${1}\${2}" +} # func_append may be replaced by extended shell implementation + +# func_append_quoted var value +# Quote VALUE and append to the end of shell variable VAR, separated +# by a space. +func_append_quoted () +{ + func_quote_for_eval "${2}" + eval "${1}=\$${1}\\ \$func_quote_for_eval_result" +} # func_append_quoted may be replaced by extended shell implementation + + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=`expr "${@}"` +} # func_arith may be replaced by extended shell implementation + + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len` +} # func_len may be replaced by extended shell implementation + + +# func_lo2o object +func_lo2o () +{ + func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` +} # func_lo2o may be replaced by extended shell implementation + + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` +} # func_xform may be replaced by extended shell implementation + + +# func_fatal_configuration arg... +# Echo program name prefixed message to standard error, followed by +# a configuration failure hint, and exit. +func_fatal_configuration () +{ + func_error ${1+"$@"} + func_error "See the $PACKAGE documentation for more information." + func_fatal_error "Fatal configuration error." +} + + +# func_config +# Display the configuration for all the tags in this script. +func_config () +{ + re_begincf='^# ### BEGIN LIBTOOL' + re_endcf='^# ### END LIBTOOL' + + # Default configuration. + $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" + + # Now print the configurations for the tags. + for tagname in $taglist; do + $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" + done + + exit $? +} + +# func_features +# Display the features supported by this script. +func_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 $? +} + +# func_enable_tag tagname +# Verify that TAGNAME is valid, and either flag an error and exit, or +# enable the TAGNAME tag. We also add TAGNAME to the global $taglist +# variable here. +func_enable_tag () +{ + # Global variable: + tagname="$1" + + re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" + re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" + sed_extractcf="/$re_begincf/,/$re_endcf/p" + + # Validate tagname. + case $tagname in + *[!-_A-Za-z0-9,/]*) + func_fatal_error "invalid tag name: $tagname" + ;; + esac + + # Don't test for the "default" C tag, as we know it's + # there but not specially marked. + case $tagname in + CC) ;; + *) + if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then + taglist="$taglist $tagname" + + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + +# func_check_version_match +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + fi + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +# Shorthand for --mode=foo, only valid as the first argument +case $1 in +clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; +compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; +execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; +finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; +install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; +link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; +uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift + ;; +esac + + + +# Option defaults: +opt_debug=: +opt_dry_run=false +opt_config=false +opt_preserve_dup_deps=false +opt_features=false +opt_finish=false +opt_help=false +opt_help_all=false +opt_silent=: +opt_warning=: +opt_verbose=: +opt_silent=false +opt_verbose=false + + +# Parse options once, thoroughly. This comes as soon as possible in the +# script to make things like `--version' happen as quickly as we can. +{ + # this just eases exit handling + while test $# -gt 0; do + opt="$1" + shift + case $opt in + --debug|-x) opt_debug='set -x' + func_echo "enabling shell trace mode" + $opt_debug + ;; + --dry-run|--dryrun|-n) + opt_dry_run=: + ;; + --config) + opt_config=: +func_config + ;; + --dlopen|-dlopen) + optarg="$1" + opt_dlopen="${opt_dlopen+$opt_dlopen +}$optarg" + shift + ;; + --preserve-dup-deps) + opt_preserve_dup_deps=: + ;; + --features) + opt_features=: +func_features + ;; + --finish) + opt_finish=: +set dummy --mode finish ${1+"$@"}; shift + ;; + --help) + opt_help=: + ;; + --help-all) + opt_help_all=: +opt_help=': help-all' + ;; + --mode) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_mode="$optarg" +case $optarg in + # Valid mode arguments: + clean|compile|execute|finish|install|link|relink|uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $opt" + exit_cmd=exit + break + ;; +esac + shift + ;; + --no-silent|--no-quiet) + opt_silent=false +func_append preserve_args " $opt" + ;; + --no-warning|--no-warn) + opt_warning=false +func_append preserve_args " $opt" + ;; + --no-verbose) + opt_verbose=false +func_append preserve_args " $opt" + ;; + --silent|--quiet) + opt_silent=: +func_append preserve_args " $opt" + opt_verbose=false + ;; + --verbose|-v) + opt_verbose=: +func_append preserve_args " $opt" +opt_silent=false + ;; + --tag) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_tag="$optarg" +func_append preserve_args " $opt $optarg" +func_enable_tag "$optarg" + shift + ;; + + -\?|-h) func_usage ;; + --help) func_help ;; + --version) func_version ;; + + # Separate optargs to long options: + --*=*) + func_split_long_opt "$opt" + set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} + shift + ;; + + # Separate non-argument short options: + -\?*|-h*|-n*|-v*) + func_split_short_opt "$opt" + set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + --) break ;; + -*) func_fatal_help "unrecognized option \`$opt'" ;; + *) set dummy "$opt" ${1+"$@"}; shift; break ;; + esac + done + + # Validate options: + + # save first non-option argument + if test "$#" -gt 0; then + nonopt="$opt" + shift + fi + + # preserve --debug + test "$opt_debug" = : || func_append preserve_args " --debug" + + case $host in + *cygwin* | *mingw* | *pw32* | *cegcc*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + ;; + esac + + $opt_help || { + # Sanity checks first: + func_check_version_match + + if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + func_fatal_configuration "not configured to build any kind of library" + fi + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$opt_dlopen" && test "$opt_mode" != execute; then + func_error "unrecognized option \`-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$progname --help --mode=$opt_mode' for more information." + } + + + # Bail if the options were screwed + $exit_cmd $EXIT_FAILURE +} + + + + +## ----------- ## +## Main. ## +## ----------- ## + +# func_lalib_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null \ + | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + +# func_lalib_unsafe_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if `file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case "$lalib_p_line" in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test "$lalib_p" = yes +} + +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + func_lalib_p "$1" +} + +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} + +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" +} + +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} + + +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $opt_debug + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$save_ifs + eval cmd=\"$cmd\" + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# `FILE.' does not work on cygwin managed mounts. +func_source () +{ + $opt_debug + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_resolve_sysroot PATH +# Replace a leading = in PATH with a sysroot. Store the result into +# func_resolve_sysroot_result +func_resolve_sysroot () +{ + func_resolve_sysroot_result=$1 + case $func_resolve_sysroot_result in + =*) + func_stripname '=' '' "$func_resolve_sysroot_result" + func_resolve_sysroot_result=$lt_sysroot$func_stripname_result + ;; + esac +} + +# func_replace_sysroot PATH +# If PATH begins with the sysroot, replace it with = and +# store the result into func_replace_sysroot_result. +func_replace_sysroot () +{ + case "$lt_sysroot:$1" in + ?*:"$lt_sysroot"*) + func_stripname "$lt_sysroot" '' "$1" + func_replace_sysroot_result="=$func_stripname_result" + ;; + *) + # Including no sysroot. + func_replace_sysroot_result=$1 + ;; + esac +} + +# 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 () +{ + $opt_debug + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + 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 "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; + # 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. + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case "$@ " in + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) + # 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 + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with \`--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=${1} + if test "$build_libtool_libs" = yes; then + write_lobj=\'${2}\' + else + write_lobj=none + fi + + if test "$build_old_libs" = yes; then + write_oldobj=\'${3}\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T <<EOF +# $write_libobj - a libtool object file +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object=$write_lobj + +# Name of the non-PIC object +non_pic_object=$write_oldobj + +EOF + $MV "${write_libobj}T" "${write_libobj}" + } +} + + +################################################## +# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS # +################################################## + +# func_convert_core_file_wine_to_w32 ARG +# Helper function used by file name conversion functions when $build is *nix, +# and $host is mingw, cygwin, or some other w32 environment. Relies on a +# correctly configured wine environment available, with the winepath program +# in $build's $PATH. +# +# ARG is the $build file name to be converted to w32 format. +# Result is available in $func_convert_core_file_wine_to_w32_result, and will +# be empty on error (or when ARG is empty) +func_convert_core_file_wine_to_w32 () +{ + $opt_debug + func_convert_core_file_wine_to_w32_result="$1" + if test -n "$1"; then + # Unfortunately, winepath does not exit with a non-zero error code, so we + # are forced to check the contents of stdout. On the other hand, if the + # command is not found, the shell will set an exit code of 127 and print + # *an error message* to stdout. So we must check for both error code of + # zero AND non-empty stdout, which explains the odd construction: + func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null` + if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then + func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | + $SED -e "$lt_sed_naive_backslashify"` + else + func_convert_core_file_wine_to_w32_result= + fi + fi +} +# end: func_convert_core_file_wine_to_w32 + + +# func_convert_core_path_wine_to_w32 ARG +# Helper function used by path conversion functions when $build is *nix, and +# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly +# configured wine environment available, with the winepath program in $build's +# $PATH. Assumes ARG has no leading or trailing path separator characters. +# +# ARG is path to be converted from $build format to win32. +# Result is available in $func_convert_core_path_wine_to_w32_result. +# Unconvertible file (directory) names in ARG are skipped; if no directory names +# are convertible, then the result may be empty. +func_convert_core_path_wine_to_w32 () +{ + $opt_debug + # unfortunately, winepath doesn't convert paths, only file names + func_convert_core_path_wine_to_w32_result="" + if test -n "$1"; then + oldIFS=$IFS + IFS=: + for func_convert_core_path_wine_to_w32_f in $1; do + IFS=$oldIFS + func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" + if test -n "$func_convert_core_file_wine_to_w32_result" ; then + if test -z "$func_convert_core_path_wine_to_w32_result"; then + func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" + else + func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" + fi + fi + done + IFS=$oldIFS + fi +} +# end: func_convert_core_path_wine_to_w32 + + +# func_cygpath ARGS... +# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when +# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) +# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or +# (2), returns the Cygwin file name or path in func_cygpath_result (input +# file name or path is assumed to be in w32 format, as previously converted +# from $build's *nix or MSYS format). In case (3), returns the w32 file name +# or path in func_cygpath_result (input file name or path is assumed to be in +# Cygwin format). Returns an empty string on error. +# +# ARGS are passed to cygpath, with the last one being the file name or path to +# be converted. +# +# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH +# environment variable; do not put it in $PATH. +func_cygpath () +{ + $opt_debug + if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then + func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` + if test "$?" -ne 0; then + # on failure, ensure result is empty + func_cygpath_result= + fi + else + func_cygpath_result= + func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" + fi +} +#end: func_cygpath + + +# func_convert_core_msys_to_w32 ARG +# Convert file name or path ARG from MSYS format to w32 format. Return +# result in func_convert_core_msys_to_w32_result. +func_convert_core_msys_to_w32 () +{ + $opt_debug + # awkward: cmd appends spaces to result + func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | + $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` +} +#end: func_convert_core_msys_to_w32 + + +# func_convert_file_check ARG1 ARG2 +# Verify that ARG1 (a file name in $build format) was converted to $host +# format in ARG2. Otherwise, emit an error message, but continue (resetting +# func_to_host_file_result to ARG1). +func_convert_file_check () +{ + $opt_debug + if test -z "$2" && test -n "$1" ; then + func_error "Could not determine host file name corresponding to" + func_error " \`$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_file_result="$1" + fi +} +# end func_convert_file_check + + +# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH +# Verify that FROM_PATH (a path in $build format) was converted to $host +# format in TO_PATH. Otherwise, emit an error message, but continue, resetting +# func_to_host_file_result to a simplistic fallback value (see below). +func_convert_path_check () +{ + $opt_debug + if test -z "$4" && test -n "$3"; then + func_error "Could not determine the host path corresponding to" + func_error " \`$3'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This is a deliberately simplistic "conversion" and + # should not be "improved". See libtool.info. + if test "x$1" != "x$2"; then + lt_replace_pathsep_chars="s|$1|$2|g" + func_to_host_path_result=`echo "$3" | + $SED -e "$lt_replace_pathsep_chars"` + else + func_to_host_path_result="$3" + fi + fi +} +# end func_convert_path_check + + +# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG +# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT +# and appending REPL if ORIG matches BACKPAT. +func_convert_path_front_back_pathsep () +{ + $opt_debug + case $4 in + $1 ) func_to_host_path_result="$3$func_to_host_path_result" + ;; + esac + case $4 in + $2 ) func_append func_to_host_path_result "$3" + ;; + esac +} +# end func_convert_path_front_back_pathsep + + +################################################## +# $build to $host FILE NAME CONVERSION FUNCTIONS # +################################################## +# invoked via `$to_host_file_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# Result will be available in $func_to_host_file_result. + + +# func_to_host_file ARG +# Converts the file name ARG from $build format to $host format. Return result +# in func_to_host_file_result. +func_to_host_file () +{ + $opt_debug + $to_host_file_cmd "$1" +} +# end func_to_host_file + + +# func_to_tool_file ARG LAZY +# converts the file name ARG from $build format to toolchain format. Return +# result in func_to_tool_file_result. If the conversion in use is listed +# in (the comma separated) LAZY, no conversion takes place. +func_to_tool_file () +{ + $opt_debug + case ,$2, in + *,"$to_tool_file_cmd",*) + func_to_tool_file_result=$1 + ;; + *) + $to_tool_file_cmd "$1" + func_to_tool_file_result=$func_to_host_file_result + ;; + esac +} +# end func_to_tool_file + + +# func_convert_file_noop ARG +# Copy ARG to func_to_host_file_result. +func_convert_file_noop () +{ + func_to_host_file_result="$1" +} +# end func_convert_file_noop + + +# func_convert_file_msys_to_w32 ARG +# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_file_result. +func_convert_file_msys_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_to_host_file_result="$func_convert_core_msys_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_w32 + + +# func_convert_file_cygwin_to_w32 ARG +# Convert file name ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_file_cygwin_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # because $build is cygwin, we call "the" cygpath in $PATH; no need to use + # LT_CYGPATH in this case. + func_to_host_file_result=`cygpath -m "$1"` + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_cygwin_to_w32 + + +# func_convert_file_nix_to_w32 ARG +# Convert file name ARG from *nix to w32 format. Requires a wine environment +# and a working winepath. Returns result in func_to_host_file_result. +func_convert_file_nix_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_file_wine_to_w32 "$1" + func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_w32 + + +# func_convert_file_msys_to_cygwin ARG +# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_file_msys_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_cygpath -u "$func_convert_core_msys_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_cygwin + + +# func_convert_file_nix_to_cygwin ARG +# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed +# in a wine environment, working winepath, and LT_CYGPATH set. Returns result +# in func_to_host_file_result. +func_convert_file_nix_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. + func_convert_core_file_wine_to_w32 "$1" + func_cygpath -u "$func_convert_core_file_wine_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_cygwin + + +############################################# +# $build to $host PATH CONVERSION FUNCTIONS # +############################################# +# invoked via `$to_host_path_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# The result will be available in $func_to_host_path_result. +# +# Path separators are also converted from $build format to $host format. If +# ARG begins or ends with a path separator character, it is preserved (but +# converted to $host format) on output. +# +# All path conversion functions are named using the following convention: +# file name conversion function : func_convert_file_X_to_Y () +# path conversion function : func_convert_path_X_to_Y () +# where, for any given $build/$host combination the 'X_to_Y' value is the +# same. If conversion functions are added for new $build/$host combinations, +# the two new functions must follow this pattern, or func_init_to_host_path_cmd +# will break. + + +# func_init_to_host_path_cmd +# Ensures that function "pointer" variable $to_host_path_cmd is set to the +# appropriate value, based on the value of $to_host_file_cmd. +to_host_path_cmd= +func_init_to_host_path_cmd () +{ + $opt_debug + if test -z "$to_host_path_cmd"; then + func_stripname 'func_convert_file_' '' "$to_host_file_cmd" + to_host_path_cmd="func_convert_path_${func_stripname_result}" + fi +} + + +# func_to_host_path ARG +# Converts the path ARG from $build format to $host format. Return result +# in func_to_host_path_result. +func_to_host_path () +{ + $opt_debug + func_init_to_host_path_cmd + $to_host_path_cmd "$1" +} +# end func_to_host_path + + +# func_convert_path_noop ARG +# Copy ARG to func_to_host_path_result. +func_convert_path_noop () +{ + func_to_host_path_result="$1" +} +# end func_convert_path_noop + + +# func_convert_path_msys_to_w32 ARG +# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_path_result. +func_convert_path_msys_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from ARG. MSYS + # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; + # and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_msys_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_msys_to_w32 + + +# func_convert_path_cygwin_to_w32 ARG +# Convert path ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_path_cygwin_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_cygwin_to_w32 + + +# func_convert_path_nix_to_w32 ARG +# Convert path ARG from *nix to w32 format. Requires a wine environment and +# a working winepath. Returns result in func_to_host_file_result. +func_convert_path_nix_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_nix_to_w32 + + +# func_convert_path_msys_to_cygwin ARG +# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_path_msys_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_msys_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_msys_to_cygwin + + +# func_convert_path_nix_to_cygwin ARG +# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a +# a wine environment, working winepath, and LT_CYGPATH set. Returns result in +# func_to_host_file_result. +func_convert_path_nix_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_nix_to_cygwin + + +# func_mode_compile arg... +func_mode_compile () +{ + $opt_debug + # 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= + pie_flag= + + 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) + test -n "$libobj" && \ + func_fatal_error "you cannot specify \`-o' more than once" + arg_mode=target + continue + ;; + + -pie | -fpie | -fPIE) + func_append pie_flag " $arg" + continue + ;; + + -shared | -static | -prefer-pic | -prefer-non-pic) + func_append 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,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + func_append_quoted lastarg "$arg" + done + IFS="$save_ifs" + func_stripname ' ' '' "$lastarg" + lastarg=$func_stripname_result + + # Add the arguments to base_compile. + func_append 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. + func_append_quoted base_compile "$lastarg" + done # for arg + + case $arg_mode in + arg) + func_fatal_error "you must specify an argument for -Xcompile" + ;; + target) + func_fatal_error "you must specify a target with \`-o'" + ;; + *) + # Get the name of the library object. + test -z "$libobj" && { + func_basename "$srcfile" + libobj="$func_basename_result" + } + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + case $libobj in + *.[cCFSifmso] | \ + *.ada | *.adb | *.ads | *.asm | \ + *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ + *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) + func_xform "$libobj" + libobj=$func_xform_result + ;; + esac + + case $libobj in + *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; + *) + func_fatal_error "cannot determine name of library object from \`$libobj'" + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + continue + ;; + + -static) + build_libtool_libs=no + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + func_quote_for_eval "$libobj" + test "X$libobj" != "X$func_quote_for_eval_result" \ + && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && func_warning "libobj name \`$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname="$func_basename_result" + xdir="$func_dirname_result" + lobj=${xdir}$objdir/$objname + + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" + + # 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 + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2* | cegcc*) + 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 "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + 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 $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "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." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + func_append removelist " $output_obj" + $ECHO "$srcfile" > "$lockfile" + fi + + $opt_dry_run || $RM $removelist + func_append removelist " $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + + func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 + srcfile=$func_to_tool_file_result + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result + + # Only build a PIC object if we are building libtool libraries. + if test "$build_libtool_libs" = yes; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test "$pic_mode" != no; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + func_mkdir_p "$xdir$objdir" + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + func_append command " -o $lobj" + fi + + func_show_eval_locale "$command" \ + 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/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." + + $opt_dry_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 + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + + # Allow error messages only from the first compilation. + if test "$suppress_opt" = yes; then + suppress_output=' >/dev/null 2>&1' + fi + fi + + # Only build a position-dependent object if we build old libraries. + if test "$build_old_libs" = yes; then + if test "$pic_mode" != yes; then + # Don't build PIC code + command="$base_compile $qsrcfile$pie_flag" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test "$compiler_c_o" = yes; then + func_append command " -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + func_append command "$suppress_output" + func_show_eval_locale "$command" \ + '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/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." + + $opt_dry_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 + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + fi + + $opt_dry_run || { + func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" + + # Unlock the critical section if it was locked + if test "$need_locks" != no; then + removelist=$lockfile + $RM "$lockfile" + fi + } + + exit $EXIT_SUCCESS +} + +$opt_help || { + test "$opt_mode" = compile && func_mode_compile ${1+"$@"} +} + +func_mode_help () +{ + # We need to display help for each of the modes. + case $opt_mode in + "") + # Generic help is extracted from the usage comments + # at the start of this file. + func_help + ;; + + clean) + $ECHO \ +"Usage: $progname [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: $progname [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 + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to build PIC objects only + -prefer-non-pic try to build non-PIC objects only + -shared do not build a \`.o' file suitable for static linking + -static only build a \`.o' file suitable for static linking + -Wc,FLAG pass FLAG directly to the compiler + +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: $progname [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: $progname [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: $progname [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 following components of INSTALL-COMMAND are treated specially: + + -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + + link) + $ECHO \ +"Usage: $progname [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 + -bindir BINDIR specify path to binaries directory (for systems where + libraries must be found in the PATH setting at runtime) + -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 + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface + -Wc,FLAG + -Xcompiler FLAG pass linker-specific FLAG directly to the compiler + -Wl,FLAG + -Xlinker FLAG pass linker-specific FLAG directly to the linker + -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) + +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: $progname [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." + ;; + + *) + func_fatal_help "invalid operation mode \`$opt_mode'" + ;; + esac + + echo + $ECHO "Try \`$progname --help' for more information about other modes." +} + +# Now that we've collected a possible --mode arg, show help if necessary +if $opt_help; then + if test "$opt_help" = :; then + func_mode_help + else + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + func_mode_help + done + } | sed -n '1p; 2,$s/^Usage:/ or: /p' + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + echo + func_mode_help + done + } | + sed '1d + /^When reporting/,/^Report/{ + H + d + } + $x + /information about other modes/d + /more detailed .*MODE/d + s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' + fi + exit $? +fi + + +# func_mode_execute arg... +func_mode_execute () +{ + $opt_debug + # The first argument is the command name. + cmd="$nonopt" + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" + + # Handle -dlopen flags immediately. + for file in $opt_dlopen; do + test -f "$file" \ + || func_fatal_help "\`$file' is not a file" + + dir= + case $file in + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$lib' is not a valid libtool archive" + + # Read the libtool library. + dlname= + library_names= + func_source "$file" + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "\`$file' was not linked with \`-export-dynamic'" + continue + fi + + func_dirname "$file" "" "." + dir="$func_dirname_result" + + if test -f "$dir/$objdir/$dlname"; then + func_append dir "/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" + fi + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir="$func_dirname_result" + ;; + + *) + func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" + 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 + -* | *.la | *.lo ) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file="$progdir/$program" + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_append_quoted args "$file" + done + + if test "X$opt_dry_run" = Xfalse; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done + + # 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 + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = execute && func_mode_execute ${1+"$@"} + + +# func_mode_finish arg... +func_mode_finish () +{ + $opt_debug + libs= + libdirs= + admincmds= + + for opt in "$nonopt" ${1+"$@"} + do + if test -d "$opt"; then + func_append libdirs " $opt" + + elif test -f "$opt"; then + if func_lalib_unsafe_p "$opt"; then + func_append libs " $opt" + else + func_warning "\`$opt' is not a valid libtool archive" + fi + + else + func_fatal_error "invalid argument \`$opt'" + fi + done + + if test -n "$libs"; then + if test -n "$lt_sysroot"; then + sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` + sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" + else + sysroot_cmd= + fi + + # Remove sysroot references + if $opt_dry_run; then + for lib in $libs; do + echo "removing references to $lt_sysroot and \`=' prefixes from $lib" + done + else + tmpdir=`func_mktempdir` + for lib in $libs; do + sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ + > $tmpdir/tmp-la + mv -f $tmpdir/tmp-la $lib + done + ${RM}r "$tmpdir" + fi + fi + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || func_append admincmds " + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + $opt_silent && exit $EXIT_SUCCESS + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + 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" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" + echo "pages." + ;; + *) + echo "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + echo "----------------------------------------------------------------------" + fi + exit $EXIT_SUCCESS +} + +test "$opt_mode" = finish && func_mode_finish ${1+"$@"} + + +# func_mode_install arg... +func_mode_install () +{ + $opt_debug + # 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. + case $nonopt in *shtool*) :;; *) false;; esac; then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + func_append install_prog "$func_quote_for_eval_result" + install_shared_prog=$install_prog + case " $install_prog " in + *[\\\ /]cp\ *) install_cp=: ;; + *) install_cp=false ;; + esac + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + no_mode=: + for arg + do + arg2= + if test -n "$dest"; then + func_append files " $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) + if $install_cp; then :; else + prev=$arg + fi + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + if test "x$prev" = x-m && test -n "$install_override_mode"; then + arg2=$install_override_mode + no_mode=false + fi + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + func_append install_prog " $func_quote_for_eval_result" + if test -n "$arg2"; then + func_quote_for_eval "$arg2" + fi + func_append install_shared_prog " $func_quote_for_eval_result" + done + + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" + + test -n "$prev" && \ + func_fatal_help "the \`$prev' option requires an argument" + + if test -n "$install_override_mode" && $no_mode; then + if $install_cp; then :; else + func_quote_for_eval "$install_override_mode" + func_append install_shared_prog " -m $func_quote_for_eval_result" + fi + fi + + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" + fi + fi + + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir="$func_dirname_result" + destname="$func_basename_result" + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "\`$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "\`$destdir' must be an absolute directory name" + ;; + 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. + func_append staticlibs " $file" + ;; + + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) func_append current_libdirs " $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) func_append future_libdirs " $libdir" ;; + esac + fi + + func_dirname "$file" "/" "" + dir="$func_dirname_result" + func_append 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 -e "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. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" + + 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 + + func_warning "relinking \`$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname="$1" + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme="$stripme" + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme="" + ;; + esac + ;; + esac + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try `ln -sf' first, because the `ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + func_execute_cmds "$postinstall_cmds" 'exit $?' + fi + + # Install the pseudo-library for information purposes. + func_basename "$file" + name="$func_basename_result" + instname="$dir/$name"i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' + + # Maybe install the static library, too. + test -n "$old_library" && func_append 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 + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to \`$destfile'" + ;; + esac + + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_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 + func_basename "$file" + destfile="$func_basename_result" + 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 + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= + + func_source "$wrapper" + + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script \`$wrapper'" + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "\`$lib' has not been installed in \`$libdir'" + finalize=no + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + $opt_dry_run || { + if test "$finalize" = yes; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file="$func_basename_result" + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_silent || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink \`$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file="$outputname" + else + func_warning "cannot relink \`$file'" + fi + } + else + # Install the binary that we compiled earlier. + file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac + ;; + esac + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done + + for file in $staticlibs; do + func_basename "$file" + name="$func_basename_result" + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $tool_oldlib" 'exit $?' + fi + + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done + + test -n "$future_libdirs" && \ + func_warning "remember to run \`$progname --finish$future_libdirs'" + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = install && func_mode_install ${1+"$@"} + + +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $opt_debug + my_outputname="$1" + my_originator="$2" + my_pic_p="${3-no}" + my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms="${my_outputname}S.c" + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi + + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${my_outputname}.nm" + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) +#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" +#endif + +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + func_verbose "generating symbol list for \`$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_to_tool_file "$progfile" func_convert_file_msys_to_w32 + func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" + $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$outputname.exp" + $opt_dry_run || { + $RM $export_symbols + eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + } + else + $opt_dry_run || { + eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + } + fi + fi + + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from \`$dlprefile'" + func_basename "$dlprefile" + name="$func_basename_result" + case $host in + *cygwin* | *mingw* | *cegcc* ) + # if an import library, we need to obtain dlname + if func_win32_import_lib_p "$dlprefile"; then + func_tr_sh "$dlprefile" + eval "curr_lafile=\$libfile_$func_tr_sh_result" + dlprefile_dlbasename="" + if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then + # Use subshell, to avoid clobbering current variable values + dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` + if test -n "$dlprefile_dlname" ; then + func_basename "$dlprefile_dlname" + dlprefile_dlbasename="$func_basename_result" + else + # no lafile. user explicitly requested -dlpreopen <import library>. + $sharedlib_from_linklib_cmd "$dlprefile" + dlprefile_dlbasename=$sharedlib_from_linklib_result + fi + fi + $opt_dry_run || { + if test -n "$dlprefile_dlbasename" ; then + eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' + else + func_warning "Could not compute DLL name from $name" + eval '$ECHO ": $name " >> "$nlist"' + fi + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | + $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" + } + else # not an import lib + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + fi + ;; + *) + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + ;; + esac + done + + $opt_dry_run || { + # 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 >/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/$my_dlsyms"' + else + echo '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi + + echo >> "$output_objdir/$my_dlsyms" "\ + +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +extern LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[]; +LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{\ + { \"$my_originator\", (void *) 0 }," + + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + echo >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + 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*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + if test "X$my_pic_p" != Xno; then + pic_flag_for_symtable=" $pic_flag" + fi + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) func_append symtab_cflags " $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' + + # Transform the symbol file into the correct name. + symfileobj="$output_objdir/${my_outputname}S.$objext" + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + else + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + fi + ;; + *) + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for \`$my_dlsyms'" + ;; + 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 "$compile_command" | $SED "s% @SYMFILE@%%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` + fi +} + +# 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. +# Despite the name, also deal with 64 bit binaries. +func_win32_libid () +{ + $opt_debug + 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 + # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then + func_to_tool_file "$1" func_convert_file_msys_to_w32 + win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | + $SED -n -e ' + 1,100{ + / I /{ + s,.*,import, + p + q + } + }'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + 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_cygming_dll_for_implib ARG +# +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib () +{ + $opt_debug + sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` +} + +# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs +# +# The is the core of a fallback implementation of a +# platform-specific function to extract the name of the +# DLL associated with the specified import library LIBNAME. +# +# SECTION_NAME is either .idata$6 or .idata$7, depending +# on the platform and compiler that created the implib. +# +# Echos the name of the DLL associated with the +# specified import library. +func_cygming_dll_for_implib_fallback_core () +{ + $opt_debug + match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` + $OBJDUMP -s --section "$1" "$2" 2>/dev/null | + $SED '/^Contents of section '"$match_literal"':/{ + # Place marker at beginning of archive member dllname section + s/.*/====MARK====/ + p + d + } + # These lines can sometimes be longer than 43 characters, but + # are always uninteresting + /:[ ]*file format pe[i]\{,1\}-/d + /^In archive [^:]*:/d + # Ensure marker is printed + /^====MARK====/p + # Remove all lines with less than 43 characters + /^.\{43\}/!d + # From remaining lines, remove first 43 characters + s/^.\{43\}//' | + $SED -n ' + # Join marker and all lines until next marker into a single line + /^====MARK====/ b para + H + $ b para + b + :para + x + s/\n//g + # Remove the marker + s/^====MARK====// + # Remove trailing dots and whitespace + s/[\. \t]*$// + # Print + /./p' | + # we now have a list, one entry per line, of the stringified + # contents of the appropriate section of all members of the + # archive which possess that section. Heuristic: eliminate + # all those which have a first or second character that is + # a '.' (that is, objdump's representation of an unprintable + # character.) This should work for all archives with less than + # 0x302f exports -- but will fail for DLLs whose name actually + # begins with a literal '.' or a single character followed by + # a '.'. + # + # Of those that remain, print the first one. + $SED -e '/^\./d;/^.\./d;q' +} + +# func_cygming_gnu_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is a GNU/binutils-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_gnu_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` + test -n "$func_cygming_gnu_implib_tmp" +} + +# func_cygming_ms_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is an MS-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_ms_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` + test -n "$func_cygming_ms_implib_tmp" +} + +# func_cygming_dll_for_implib_fallback ARG +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# +# This fallback implementation is for use when $DLLTOOL +# does not support the --identify-strict option. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib_fallback () +{ + $opt_debug + if func_cygming_gnu_implib_p "$1" ; then + # binutils import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` + elif func_cygming_ms_implib_p "$1" ; then + # ms-generated import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` + else + # unknown + sharedlib_from_linklib_result="" + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $opt_debug + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + if test "$lock_old_archive_extraction" = yes; then + lockfile=$f_ex_an_ar_oldlib.lock + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + fi + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ + 'stat=$?; rm -f "$lockfile"; exit $stat' + if test "$lock_old_archive_extraction" = yes; then + $opt_dry_run || rm -f "$lockfile" + fi + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $opt_debug + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + + 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 + func_basename "$my_xlib" + my_xlib="$func_basename_result" + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir="$my_gentop/$my_xlib_u" + + func_mkdir_p "$my_xdir" + + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + 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 | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` + done + + func_extract_archives_result="$my_oldobjs" +} + + +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory in which it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=${1-no} + + $ECHO "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# 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. +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# 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 variables: + generated_by_libtool_version='$macro_version' + 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 + file=\"\$0\"" + + qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` + $ECHO "\ + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + ECHO=\"$qECHO\" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ which is used only on +# windows platforms, and (c) all begin with the string "--lt-" +# (application programs are unlikely to have options which match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's $0 value, followed by "$@". +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=\$0 + shift + for lt_opt + do + case \"\$lt_opt\" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` + test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. + lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` + cat \"\$lt_dump_D/\$lt_dump_F\" + exit 0 + ;; + --lt-*) + \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n \"\$lt_option_debug\"; then + echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" + lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from \$@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case \" \$* \" in + *\\ --lt-*) + for lt_wr_arg + do + case \$lt_wr_arg in + --lt-*) ;; + *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; + esac + shift + done ;; + esac + func_exec_program_core \${1+\"\$@\"} +} + + # Parse options + func_parse_lt_options \"\$0\" \${1+\"\$@\"} + + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"\$file\" | $SED '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 \"\$file\" | $SED '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 \"\$file\" | $SED 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $ECHO "\ + 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 "\ + + # 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 1 + fi + fi + + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $ECHO "\ + + if test -f \"\$progdir/\$program\"; then" + + # fixup the dll searchpath if we need to. + # + # Fix the DLL searchpath if we need to. Do this before prepending + # to shlibpath, because on Windows, both are PATH and uninstalled + # libraries must come first. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # 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 \"\$$shlibpath_var\" | $SED 's/::*\$//'\` + + export $shlibpath_var +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. + func_exec_program \${1+\"\$@\"} + 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 1 + fi +fi\ +" +} + + +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat <<EOF + +/* $cwrappersource - temporary wrapper executable for $objdir/$outputname + Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION + + The $output program cannot be directly executed until all the libtool + libraries that it depends on are installed. + + This wrapper executable should never be moved out of the build directory. + If it is, it will not operate correctly. +*/ +EOF + cat <<"EOF" +#ifdef _MSC_VER +# define _CRT_SECURE_NO_DEPRECATE 1 +#endif +#include <stdio.h> +#include <stdlib.h> +#ifdef _MSC_VER +# include <direct.h> +# include <process.h> +# include <io.h> +#else +# include <unistd.h> +# include <stdint.h> +# ifdef __CYGWIN__ +# include <io.h> +# endif +#endif +#include <malloc.h> +#include <stdarg.h> +#include <assert.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> + +/* declarations of non-ANSI functions */ +#if defined(__MINGW32__) +# ifdef __STRICT_ANSI__ +int _putenv (const char *); +# endif +#elif defined(__CYGWIN__) +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +/* #elif defined (other platforms) ... */ +#endif + +/* portability defines, excluding path handling macros */ +#if defined(_MSC_VER) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +# define S_IXUSR _S_IEXEC +# ifndef _INTPTR_T_DEFINED +# define _INTPTR_T_DEFINED +# define intptr_t int +# endif +#elif defined(__MINGW32__) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +#elif defined(__CYGWIN__) +# define HAVE_SETENV +# define FOPEN_WB "wb" +/* #elif defined (other platforms) ... */ +#endif + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +/* path handling portability macros */ +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_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 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +#if defined(LT_DEBUGWRAPPER) +static int lt_debug = 1; +#else +static int lt_debug = 0; +#endif + +const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_debugprintf (const char *file, int line, const char *fmt, ...); +void lt_fatal (const char *file, int line, const char *message, ...); +static const char *nonnull (const char *s); +static const char *nonempty (const char *s); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); +char **prepare_spawn (char **argv); +void lt_dump_script (FILE *f); +EOF + + cat <<EOF +volatile const char * MAGIC_EXE = "$magic_exe"; +const char * LIB_PATH_VARNAME = "$shlibpath_var"; +EOF + + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + func_to_host_path "$temp_rpath" + cat <<EOF +const char * LIB_PATH_VALUE = "$func_to_host_path_result"; +EOF + else + cat <<"EOF" +const char * LIB_PATH_VALUE = ""; +EOF + fi + + if test -n "$dllsearchpath"; then + func_to_host_path "$dllsearchpath:" + cat <<EOF +const char * EXE_PATH_VARNAME = "PATH"; +const char * EXE_PATH_VALUE = "$func_to_host_path_result"; +EOF + else + cat <<"EOF" +const char * EXE_PATH_VARNAME = ""; +const char * EXE_PATH_VALUE = ""; +EOF + fi + + if test "$fast_install" = yes; then + cat <<EOF +const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */ +EOF + else + cat <<EOF +const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */ +EOF + fi + + + cat <<"EOF" + +#define LTWRAPPER_OPTION_PREFIX "--lt-" + +static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX; +static const char *dumpscript_opt = LTWRAPPER_OPTION_PREFIX "dump-script"; +static const char *debug_opt = LTWRAPPER_OPTION_PREFIX "debug"; + +int +main (int argc, char *argv[]) +{ + char **newargz; + int newargc; + char *tmp_pathspec; + char *actual_cwrapper_path; + char *actual_cwrapper_name; + char *target_name; + char *lt_argv_zero; + intptr_t rval = 127; + + int i; + + program_name = (char *) xstrdup (base_name (argv[0])); + newargz = XMALLOC (char *, argc + 1); + + /* very simple arg parsing; don't want to rely on getopt + * also, copy all non cwrapper options to newargz, except + * argz[0], which is handled differently + */ + newargc=0; + for (i = 1; i < argc; i++) + { + if (strcmp (argv[i], dumpscript_opt) == 0) + { +EOF + case "$host" in + *mingw* | *cygwin* ) + # make stdout use "unix" line endings + echo " setmode(1,_O_BINARY);" + ;; + esac + + cat <<"EOF" + lt_dump_script (stdout); + return 0; + } + if (strcmp (argv[i], debug_opt) == 0) + { + lt_debug = 1; + continue; + } + if (strcmp (argv[i], ltwrapper_option_prefix) == 0) + { + /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX + namespace, but it is not one of the ones we know about and + have already dealt with, above (inluding dump-script), then + report an error. Otherwise, targets might begin to believe + they are allowed to use options in the LTWRAPPER_OPTION_PREFIX + namespace. The first time any user complains about this, we'll + need to make LTWRAPPER_OPTION_PREFIX a configure-time option + or a configure.ac-settable value. + */ + lt_fatal (__FILE__, __LINE__, + "unrecognized %s option: '%s'", + ltwrapper_option_prefix, argv[i]); + } + /* otherwise ... */ + newargz[++newargc] = xstrdup (argv[i]); + } + newargz[++newargc] = NULL; + +EOF + cat <<EOF + /* The GNU banner must be the first non-error debug message */ + lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\n"); +EOF + cat <<"EOF" + lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]); + lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name); + + tmp_pathspec = find_executable (argv[0]); + if (tmp_pathspec == NULL) + lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]); + lt_debugprintf (__FILE__, __LINE__, + "(main) found exe (before symlink chase) at: %s\n", + tmp_pathspec); + + actual_cwrapper_path = chase_symlinks (tmp_pathspec); + lt_debugprintf (__FILE__, __LINE__, + "(main) found exe (after symlink chase) at: %s\n", + actual_cwrapper_path); + XFREE (tmp_pathspec); + + actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path)); + strendzap (actual_cwrapper_path, actual_cwrapper_name); + + /* wrapper name transforms */ + strendzap (actual_cwrapper_name, ".exe"); + tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1); + XFREE (actual_cwrapper_name); + actual_cwrapper_name = tmp_pathspec; + tmp_pathspec = 0; + + /* target_name transforms -- use actual target program name; might have lt- prefix */ + target_name = xstrdup (base_name (TARGET_PROGRAM_NAME)); + strendzap (target_name, ".exe"); + tmp_pathspec = lt_extend_str (target_name, ".exe", 1); + XFREE (target_name); + target_name = tmp_pathspec; + tmp_pathspec = 0; + + lt_debugprintf (__FILE__, __LINE__, + "(main) libtool target name: %s\n", + target_name); +EOF + + cat <<EOF + newargz[0] = + XMALLOC (char, (strlen (actual_cwrapper_path) + + strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1)); + strcpy (newargz[0], actual_cwrapper_path); + strcat (newargz[0], "$objdir"); + strcat (newargz[0], "/"); +EOF + + cat <<"EOF" + /* stop here, and copy so we don't have to do this twice */ + tmp_pathspec = xstrdup (newargz[0]); + + /* do NOT want the lt- prefix here, so use actual_cwrapper_name */ + strcat (newargz[0], actual_cwrapper_name); + + /* DO want the lt- prefix here if it exists, so use target_name */ + lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1); + XFREE (tmp_pathspec); + tmp_pathspec = NULL; +EOF + + case $host_os in + mingw*) + cat <<"EOF" + { + char* p; + while ((p = strchr (newargz[0], '\\')) != NULL) + { + *p = '/'; + } + while ((p = strchr (lt_argv_zero, '\\')) != NULL) + { + *p = '/'; + } + } +EOF + ;; + esac + + cat <<"EOF" + XFREE (target_name); + XFREE (actual_cwrapper_path); + XFREE (actual_cwrapper_name); + + lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */ + lt_setenv ("DUALCASE", "1"); /* for MSK sh */ + /* Update the DLL searchpath. EXE_PATH_VALUE ($dllsearchpath) must + be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath) + because on Windows, both *_VARNAMEs are PATH but uninstalled + libraries must come first. */ + lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE); + lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE); + + lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n", + nonnull (lt_argv_zero)); + for (i = 0; i < newargc; i++) + { + lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n", + i, nonnull (newargz[i])); + } + +EOF + + case $host_os in + mingw*) + cat <<"EOF" + /* execv doesn't actually work on mingw as expected on unix */ + newargz = prepare_spawn (newargz); + rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz); + if (rval == -1) + { + /* failed to start process */ + lt_debugprintf (__FILE__, __LINE__, + "(main) failed to launch target \"%s\": %s\n", + lt_argv_zero, nonnull (strerror (errno))); + return 127; + } + return rval; +EOF + ;; + *) + cat <<"EOF" + execv (lt_argv_zero, newargz); + return rval; /* =127, but avoids unused variable warning */ +EOF + ;; + esac + + cat <<"EOF" +} + +void * +xmalloc (size_t num) +{ + void *p = (void *) malloc (num); + if (!p) + lt_fatal (__FILE__, __LINE__, "memory exhausted"); + + return p; +} + +char * +xstrdup (const char *string) +{ + return string ? strcpy ((char *) xmalloc (strlen (string) + 1), + string) : NULL; +} + +const char * +base_name (const char *name) +{ + const char *base; + +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + /* Skip over the disk name in MSDOS pathnames. */ + if (isalpha ((unsigned char) name[0]) && name[1] == ':') + name += 2; +#endif + + for (base = name; *name; name++) + if (IS_DIR_SEPARATOR (*name)) + base = name + 1; + return base; +} + +int +check_executable (const char *path) +{ + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if ((stat (path, &st) >= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} + +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + int tmp_len; + char *concat_name; + + lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", + nonempty (wrapper)); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = q - p; + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} + +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + lt_debugprintf (__FILE__, __LINE__, + "checking path component for symlinks: %s\n", + tmp_pathspec); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } + + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + lt_fatal (__FILE__, __LINE__, + "error accessing file \"%s\": %s", + tmp_pathspec, nonnull (strerror (errno))); + } + } + XFREE (tmp_pathspec); + + if (!has_symlinks) + { + return xstrdup (pathspec); + } + + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal (__FILE__, __LINE__, + "could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} + +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; +} + +void +lt_debugprintf (const char *file, int line, const char *fmt, ...) +{ + va_list args; + if (lt_debug) + { + (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); + } +} + +static void +lt_error_core (int exit_status, const char *file, + int line, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *file, int line, const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); + va_end (ap); +} + +static const char * +nonnull (const char *s) +{ + return s ? s : "(null)"; +} + +static const char * +nonempty (const char *s) +{ + return (s && !*s) ? "(empty)" : nonnull (s); +} + +void +lt_setenv (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_setenv) setting '%s' to '%s'\n", + nonnull (name), nonnull (value)); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + int len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} + +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + int orig_value_len = strlen (orig_value); + int add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} + +void +lt_update_exe_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + int len = strlen (new_value); + while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[len-1] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +void +lt_update_lib_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +EOF + case $host_os in + mingw*) + cat <<"EOF" + +/* Prepares an argument vector before calling spawn(). + Note that spawn() does not by itself call the command interpreter + (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : + ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&v); + v.dwPlatformId == VER_PLATFORM_WIN32_NT; + }) ? "cmd.exe" : "command.com"). + Instead it simply concatenates the arguments, separated by ' ', and calls + CreateProcess(). We must quote the arguments since Win32 CreateProcess() + interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a + special way: + - Space and tab are interpreted as delimiters. They are not treated as + delimiters if they are surrounded by double quotes: "...". + - Unescaped double quotes are removed from the input. Their only effect is + that within double quotes, space and tab are treated like normal + characters. + - Backslashes not followed by double quotes are not special. + - But 2*n+1 backslashes followed by a double quote become + n backslashes followed by a double quote (n >= 0): + \" -> " + \\\" -> \" + \\\\\" -> \\" + */ +#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +char ** +prepare_spawn (char **argv) +{ + size_t argc; + char **new_argv; + size_t i; + + /* Count number of arguments. */ + for (argc = 0; argv[argc] != NULL; argc++) + ; + + /* Allocate new argument vector. */ + new_argv = XMALLOC (char *, argc + 1); + + /* Put quoted arguments into the new argument vector. */ + for (i = 0; i < argc; i++) + { + const char *string = argv[i]; + + if (string[0] == '\0') + new_argv[i] = xstrdup ("\"\""); + else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) + { + int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); + size_t length; + unsigned int backslashes; + const char *s; + char *quoted_string; + char *p; + + length = 0; + backslashes = 0; + if (quote_around) + length++; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + length += backslashes + 1; + length++; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + + quoted_string = XMALLOC (char, length + 1); + + p = quoted_string; + backslashes = 0; + if (quote_around) + *p++ = '"'; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + { + unsigned int j; + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + *p++ = c; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + unsigned int j; + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p = '\0'; + + new_argv[i] = quoted_string; + } + else + new_argv[i] = (char *) string; + } + new_argv[argc] = NULL; + + return new_argv; +} +EOF + ;; + esac + + cat <<"EOF" +void lt_dump_script (FILE* f) +{ +EOF + func_emit_wrapper yes | + $SED -n -e ' +s/^\(.\{79\}\)\(..*\)/\1\ +\2/ +h +s/\([\\"]\)/\\\1/g +s/$/\\n/ +s/\([^\n]*\).*/ fputs ("\1", f);/p +g +D' + cat <<"EOF" +} +EOF +} +# end: func_emit_cwrapperexe_src + +# func_win32_import_lib_p ARG +# True if ARG is an import lib, as indicated by $file_magic_cmd +func_win32_import_lib_p () +{ + $opt_debug + case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in + *import*) : ;; + *) false ;; + esac +} + +# func_mode_link arg... +func_mode_link () +{ + $opt_debug + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # which system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll which has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= + + avoid_version=no + bindir= + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=no + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module="${wl}-single_module" + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_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 + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + func_append compile_command " @OUTPUT@" + func_append finalize_command " @OUTPUT@" + ;; + esac + + case $prev in + bindir) + bindir="$arg" + prev= + continue + ;; + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + func_append compile_command " @SYMFILE@" + func_append 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 + func_append dlfiles " $arg" + else + func_append dlprefiles " $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + test -f "$arg" \ + || func_fatal_error "symbol file \`$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) func_append deplibs " $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# func_append moreargs " $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + 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 + func_append 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. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append 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 + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file \`$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) func_append rpath " $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) func_append xrpath " $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + weak) + func_append weak_libs " $arg" + prev= + continue + ;; + xcclinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xcompiler) + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xlinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $wl$qarg" + prev= + func_append compile_command " $wl$qarg" + func_append finalize_command " $wl$qarg" + 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 + # See comment for -static flag below, for more details. + func_append compile_command " $link_static_flag" + func_append finalize_command " $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "\`-allow-undefined' must not be used because it is the default" + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -bindir) + prev=bindir + 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 + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework) + prev=framework + 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*) + func_append compile_command " $arg" + func_append finalize_command " $arg" + ;; + esac + continue + ;; + + -L*) + func_stripname "-L" '' "$arg" + if test -z "$func_stripname_result"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between \`-L' and \`$1'" + else + func_fatal_error "need path for \`-L' option" + fi + fi + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of \`$dir'" + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "* | *" $arg "*) + # Will only happen for absolute or sysroot arguments + ;; + *) + # Preserve sysroot, but never include relative directories + case $dir in + [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; + *) func_append deplibs " -L$dir" ;; + esac + func_append lib_search_path " $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) func_append dllsearchpath ":$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # 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 + func_append deplibs " System.ltframework" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test "X$arg" = "X-lc" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test "X$arg" = "X-lc" && continue + ;; + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + func_append deplibs " $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + # Darwin uses the -arch flag to determine output architecture. + -model|-arch|-isysroot|--sysroot) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) func_append new_inherited_linker_flags " $arg" ;; + esac + continue + ;; + + -multi_module) + single_module="${wl}-multi_module" + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "\`-no-install' is ignored for $host" + func_warning "assuming \`-no-fast-install' instead" + 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*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + =*) + func_stripname '=' '' "$dir" + dir=$lt_sysroot$func_stripname_result + ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + continue + ;; + + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -static | -static-libtool-libs) + # 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 + ;; + + -weak) + prev=weak + continue + ;; + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + func_append arg " $func_quote_for_eval_result" + func_append compiler_flags " $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + func_append arg " $wl$func_quote_for_eval_result" + func_append compiler_flags " $wl$func_quote_for_eval_result" + func_append linker_flags " $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + # Flags to be passed through unchanged, with rationale: + # -64, -mips[0-9] enable 64-bit mode for the SGI compiler + # -r[0-9][0-9]* specify processor for the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler + # +DA*, +DD* enable 64-bit mode for the HP compiler + # -q* compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* architecture-specific flags for GCC + # -F/path path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* profiling flags for GCC + # @file GCC response files + # -tp=* Portland pgcc target processor selection + # --sysroot=* for sysroot support + # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ + -O*|-flto*|-fwhopr*|-fuse-linker-plugin) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + func_append compile_command " $arg" + func_append finalize_command " $arg" + func_append compiler_flags " $arg" + continue + ;; + + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + *.$objext) + # A standard object. + func_append objs " $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + 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 + func_append 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. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append 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 + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + func_append deplibs " $arg" + func_append old_deplibs " $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + func_resolve_sysroot "$arg" + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + func_append dlfiles " $func_resolve_sysroot_result" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + func_append dlprefiles " $func_resolve_sysroot_result" + prev= + else + func_append deplibs " $func_resolve_sysroot_result" + 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. + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the \`$prevarg' option requires an argument" + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname="$func_basename_result" + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'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\" + + func_dirname "$output" "/" "" + output_objdir="$func_dirname_result$objdir" + func_to_tool_file "$output_objdir/" + tool_output_objdir=$func_to_tool_file_result + # Create the object directory. + func_mkdir_p "$output_objdir" + + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + 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 $opt_preserve_dup_deps ; then + case "$libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append 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 $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; + esac + func_append 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 dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" + ;; + 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 + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test "$linkmode,$pass" = "lib,link"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs="$tmp_deplibs" + fi + + 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 "$linkmode,$pass" = "lib,dlpreopen"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + func_resolve_sysroot "$lib" + case $lib in + *.la) func_source "$func_resolve_sysroot_result" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + func_basename "$deplib" + deplib_base=$func_basename_result + case " $weak_libs " in + *" $deplib_base "*) ;; + *) func_append deplibs " $deplib" ;; + esac + done + done + libs="$dlprefiles" + 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 \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append compiler_flags " $deplib" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + func_warning "\`-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test "$linkmode" = lib; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; 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 func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + 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 + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + 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 + *.ltframework) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + 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 + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + *) + func_warning "\`-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + func_stripname '-R' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) + func_resolve_sysroot "$deplib" + lib=$func_resolve_sysroot_result + ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + 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 use here." + else + echo + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + ;; + esac + 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. + func_append newdlprefiles " $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append newdlfiles " $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + + if test "$found" = yes || test -f "$lib"; then : + else + func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" + fi + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "\`$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && func_append dlfiles " $dlopen" + test -n "$dlpreopen" && func_append 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 + func_fatal_error "cannot find name of link library for \`$lib'" + fi + # It is a libtool convenience library, so add in its objects. + func_append convenience " $ladir/$objdir/$old_library" + func_append old_convenience " $ladir/$objdir/$old_library" + elif test "$linkmode" != prog && test "$linkmode" != lib; then + func_fatal_error "\`$lib' is not a convenience library" + fi + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + if test -n "$old_library" && + { test "$prefer_static_libs" = yes || + test "$prefer_static_libs,$installed" = "built,no"; }; then + linklib=$old_library + else + for l in $old_library $library_names; do + linklib="$l" + done + fi + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + func_fatal_error "cannot -dlopen a convenience library: \`$lib'" + 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. + func_append dlprefiles " $lib $dependency_libs" + else + func_append 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 + func_warning "cannot determine absolute directory name of \`$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir="$ladir" + fi + ;; + esac + func_basename "$lib" + laname="$func_basename_result" + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library \`$lib' was moved." + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$lt_sysroot$libdir" + absdir="$lt_sysroot$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + func_append notinst_path " $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + func_append notinst_path " $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir" && test "$linkmode" = prog; then + func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" + fi + case "$host" in + # special handling for platforms with PE-DLLs. + *cygwin* | *mingw* | *cegcc* ) + # Linker will automatically link against shared library if both + # static and shared are present. Therefore, ensure we extract + # symbols from the import library if a shared library is present + # (otherwise, the dlopen module name will be incorrect). We do + # this by putting the import library name into $newdlprefiles. + # We recover the dlopen module name by 'saving' the la file + # name in a special purpose variable, and (later) extracting the + # dlname from the la file. + if test -n "$dlname"; then + func_tr_sh "$dir/$linklib" + eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" + func_append newdlprefiles " $dir/$linklib" + else + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + fi + ;; + * ) + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + func_append newdlprefiles " $dir/$dlname" + else + func_append newdlprefiles " $dir/$linklib" + fi + ;; + esac + 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 + func_append 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*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + 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 $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append 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 "$prefer_static_libs,$installed" = "built,yes"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath:" in + *"$absdir:"*) ;; + *) func_append temp_rpath "$absdir:" ;; + 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 "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append 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 + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc*) + # No point in relinking DLLs because paths are not encoded + func_append notinst_deplibs " $lib" + need_relink=no + ;; + *) + if test "$installed" = no; then + func_append notinst_deplibs " $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule="" + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule="$dlpremoduletest" + break + fi + done + if test -z "$dlopenmodule" && 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 "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname="$1" + 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* | *cegcc*) + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + func_basename "$soroot" + soname="$func_basename_result" + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from \`$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for \`$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + 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 "$opt_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.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we can not + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null ; then + if test "X$dlopenmodule" != "X$lib"; 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 + elif test -n "$old_library"; then + 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 && + test "$hardcode_direct_absolute" = no; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$absdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append 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 + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) func_append 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:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$opt_mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; 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:"*) ;; + *) func_append 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 + [\\/]*) + func_append 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 + 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*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) func_append xrpath " $temp_xrpath";; + esac;; + *) func_append temp_deplibs " $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + func_append 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" + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result";; + *) func_resolve_sysroot "$deplib" ;; + esac + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $func_resolve_sysroot_result "*) + func_append specialdeplibs " $func_resolve_sysroot_result" ;; + esac + fi + func_append tmp_libs " $func_resolve_sysroot_result" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + path= + case $deplib in + -L*) path="$deplib" ;; + *.la) + func_resolve_sysroot "$deplib" + deplib=$func_resolve_sysroot_result + func_dirname "$deplib" "" "." + dir=$func_dirname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of \`$dir'" + absdir="$dir" + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + 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 "$absdir/$objdir/$depdepl" ; then + depdepl="$absdir/$objdir/$depdepl" + darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" + func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}" + path= + fi + fi + ;; + *) + path="-L$absdir/$objdir" + ;; + esac + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "\`$deplib' seems to be moved" + + path="-L$absdir" + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test "$pass" = link; then + if test "$linkmode" = "prog"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + 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 "*) ;; + *) func_append 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 "*) ;; + *) func_append tmp_libs " $deplib" ;; + esac + ;; + *) func_append 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 + func_append tmp_libs " $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + fi + if test "$linkmode" = prog || test "$linkmode" = lib; then + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "\`-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "\`-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + func_append objs "$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test "$module" = no && \ + func_fatal_help "libtool library \`$output' must begin with \`lib'" + + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" + else + echo + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + func_append libobjs " $objs" + fi + fi + + test "$dlself" != no && \ + func_warning "\`-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test "$#" -gt 1 && \ + func_warning "ignoring multiple \`-rpath's for a libtool library" + + install_libdir="$1" + + 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 + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "\`-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + shift + IFS="$save_ifs" + + test -n "$7" && \ + func_fatal_help "too many parameters to \`-version-info'" + + # 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="$1" + number_minor="$2" + number_revision="$3" + # + # 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 + # correct linux to gnu/linux during the next big refactor + darwin|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|qnx|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_minor" + lt_irix_increment=no + ;; + esac + ;; + no) + current="$1" + revision="$2" + age="$3" + ;; + 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]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT \`$current' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION \`$revision' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE \`$age' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE \`$age' is greater than the current interface number \`$current'" + func_fatal_error "\`$vinfo' is not valid version information" + 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 + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current" + ;; + + irix | nonstopux) + if test "X$lt_irix_increment" = "Xno"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + 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 + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) # correct to gnu/linux during the next big refactor + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + 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 + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + func_append verstring ":${current}.0" + ;; + + qnx) + major=".$current" + versuffix=".$current" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + + *) + func_fatal_configuration "unknown library version type \`$version_type'" + ;; + 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 + func_warning "undefined symbols not allowed in $host shared libraries" + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + + fi + + func_generate_dlsyms "$libname" "$libname" "yes" + func_append libobjs " $symfileobj" + test "X$libobjs" = "X " && libobjs= + + if test "$opt_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 | *.gcno) + ;; + $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 + func_append removelist " $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + func_append oldlibs " $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` + # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` + # dependency_libs=`$ECHO "$dependency_libs " | $SED "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 + func_replace_sysroot "$libdir" + func_append temp_xrpath " -R$func_replace_sysroot_result" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append 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 "*) ;; + *) func_append 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 "*) ;; + *) func_append dlprefiles " $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + func_append deplibs " System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + func_append 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. + $opt_dry_run || $RM conftest.c + cat > conftest.c <<EOF + int main() { return 0; } +EOF + $opt_dry_run || $RM conftest + if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then + ldd_output=`ldd conftest` + for i in $deplibs; do + case $i in + -l*) + func_stripname -l '' "$i" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $i "*) + func_append newdeplibs " $i" + i="" + ;; + esac + fi + if test -n "$i" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + deplib_matches=`eval "\\$ECHO \"$library_names_spec\""` + set dummy $deplib_matches; shift + deplib_match=$1 + if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then + func_append newdeplibs " $i" + else + droppeddeps=yes + echo + $ECHO "*** Warning: dynamic linker does not accept needed library $i." + 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 I believe you do not have" + echo "*** because a test_compile did reveal that the linker did not use it for" + echo "*** its dynamic dependency list that programs get resolved with at runtime." + fi + fi + ;; + *) + func_append newdeplibs " $i" + ;; + esac + done + else + # Error occurred in the first compile. Let's try to salvage + # the situation: Compile a separate program for each library. + for i in $deplibs; do + case $i in + -l*) + func_stripname -l '' "$i" + name=$func_stripname_result + $opt_dry_run || $RM conftest + if $LTCC $LTCFLAGS -o conftest conftest.c $i; then + ldd_output=`ldd conftest` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $i "*) + func_append newdeplibs " $i" + i="" + ;; + esac + fi + if test -n "$i" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + deplib_matches=`eval "\\$ECHO \"$library_names_spec\""` + set dummy $deplib_matches; shift + deplib_match=$1 + if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then + func_append newdeplibs " $i" + else + droppeddeps=yes + echo + $ECHO "*** Warning: dynamic linker does not accept needed library $i." + 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 a test_compile did reveal that the linker did not use this one" + echo "*** as a dynamic dependency that programs can get resolved with at runtime." + fi + fi + else + droppeddeps=yes + echo + $ECHO "*** Warning! Library $i is needed by this library but I was not able to" + echo "*** make it link in! You will probably need to install it or some" + echo "*** library that it depends on before this library will be fully" + echo "*** functional. Installing it before continuing would be even better." + fi + ;; + *) + func_append newdeplibs " $i" + ;; + esac + done + fi + ;; + file_magic*) + set dummy $deplibs_check_method; shift + file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + func_append newdeplibs " $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + if test -n "$file_magic_glob"; then + libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob` + else + libnameglob=$libname + fi + test "$want_nocaseglob" = yes && nocaseglob=`shopt -p nocaseglob` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + if test "$want_nocaseglob" = yes; then + shopt -s nocaseglob + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + $nocaseglob + else + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + fi + 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 "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + func_append 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 + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + func_append 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 + func_append 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 + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; 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 " $tmp_deplibs" | $SED "s,$i,,"` + done + fi + case $tmp_deplibs in + *[!\ \ ]*) + 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 + ;; + esac + ;; + 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 with the System framework + newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + 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 + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + deplibs="$new_libs" + + # 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 + # Remove ${wl} instances when linking with ld. + # FIXME: should test the right _cmds variable. + case $archive_cmds in + *\$LD\ *) wl= ;; + esac + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$opt_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 + func_replace_sysroot "$libdir" + libdir=$func_replace_sysroot_result + 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"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append dep_rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append 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 "dep_rpath=\"$hardcode_libdir_flag_spec\"" + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append 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 "$opt_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 + shift + realname="$1" + 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" + linknames= + for link + do + func_append linknames " $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= + + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols="$output_objdir/$libname.uexp" + func_append delfiles " $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + if test "x`$SED 1q $export_symbols`" != xEXPORTS; then + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols="$export_symbols" + export_symbols= + always_export_symbols=yes + fi + fi + ;; + esac + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd1 in $cmds; do + IFS="$save_ifs" + # Take the normal branch if the nm_file_list_spec branch + # doesn't work or if tool conversion is not needed. + case $nm_file_list_spec~$to_tool_file_cmd in + *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) + try_normal_branch=yes + eval cmd=\"$cmd1\" + func_len " $cmd" + len=$func_len_result + ;; + *) + try_normal_branch=no + ;; + esac + if test "$try_normal_branch" = yes \ + && { test "$len" -lt "$max_cmd_len" \ + || test "$max_cmd_len" -le -1; } + then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + elif test -n "$nm_file_list_spec"; then + func_basename "$output" + output_la=$func_basename_result + save_libobjs=$libobjs + save_output=$output + output=${output_objdir}/${output_la}.nm + func_to_tool_file "$output" + libobjs=$nm_file_list_spec$func_to_tool_file_result + func_append delfiles " $output" + func_verbose "creating $NM input file list: $output" + for obj in $save_libobjs; do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > "$output" + eval cmd=\"$cmd1\" + func_show_eval "$cmd" 'exit $?' + output=$save_output + libobjs=$save_libobjs + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + func_append tmp_deplibs " $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test "$compiler_needs_object" = yes && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + func_append linker_flags " $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$opt_mode" = relink; then + $opt_dry_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:" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. + + # 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 + func_basename "$output" + output_la=$func_basename_result + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 + + if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then + output=${output_objdir}/${output_la}.lnkscript + func_verbose "creating GNU ld script: $output" + echo 'INPUT (' > $output + for obj in $save_libobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + echo ')' >> $output + func_append delfiles " $output" + func_to_tool_file "$output" + output=$func_to_tool_file_result + elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then + output=${output_objdir}/${output_la}.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test "$compiler_needs_object" = yes; then + firstobj="$1 " + shift + fi + for obj + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + func_append delfiles " $output" + func_to_tool_file "$output" + output=$firstobj\"$file_list_spec$func_to_tool_file_result\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-${k}.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test "X$objlist" = X || + test "$len" -lt "$max_cmd_len"; then + func_append 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. + reload_objs=$objlist + eval concat_cmds=\"$reload_cmds\" + else + # All subsequent reloadable object files will link in + # the last one created. + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-${k}.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-${k}.$objext + objlist=" $obj" + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + 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~ + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\${concat_cmds}$reload_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" + fi + func_append delfiles " $output" + + else + output= + fi + + if ${skipped_export-false}; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + fi + + test -n "$save_libobjs" && + func_verbose "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" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + + if ${skipped_export-false}; then + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + fi + + 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\" + test "X$libobjs" = "X " && libobjs= + 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 + fi + + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_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 "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for objects" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "\`-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object \`$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $opt_dry_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 and hope we can get by with + # turning comma into space.. + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` + else + gentop="$output_objdir/${obj}x" + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # If we're not building shared, we need to use non_pic_objs + test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + func_execute_cmds "$reload_cmds" 'exit $?' + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + func_show_eval '${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" + # $opt_dry_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" + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for programs" + + test -n "$release" && \ + func_warning "\`-release' is ignored for programs" + + test "$preload" = yes \ + && test "$dlopen_support" = unknown \ + && test "$dlopen_self" = unknown \ + && test "$dlopen_self_static" = unknown && \ + func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test "$tagname" = CXX ; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + func_append compile_command " ${wl}-bind_at_load" + func_append finalize_command " ${wl}-bind_at_load" + ;; + esac + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + compile_deplibs="$new_libs" + + + func_append compile_command " $compile_deplibs" + func_append 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 "*) ;; + *) func_append 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"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) func_append dllsearchpath ":$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + 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"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) func_append 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 "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + fi + + func_generate_dlsyms "$outputname" "@PROGRAM@" "no" + + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi + + wrappers_required=yes + case $host in + *cegcc* | *mingw32ce*) + # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. + wrappers_required=no + ;; + *cygwin* | *mingw* ) + if test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + *) + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + esac + if test "$wrappers_required" = no; then + # Replace the output file specification. + compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.${objext}"; then + func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' + fi + + exit $exit_status + 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 + func_append 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 + func_append 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 "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + 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" + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "\`$output' will be relinked during installation" + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED '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 "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname + + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output_objdir/$outputname" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Now create the wrapper script. + func_verbose "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}\" || $lt_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 + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource="$output_path/$objdir/lt-$output_name.c" + cwrapper="$output_path/$output_name.exe" + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } + + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host" ; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 + + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + 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 $symfileobj" + 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" + if test "$preload" = yes && test -f "$symfileobj"; then + func_append oldobjs " $symfileobj" + fi + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $addlibs + func_append 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 + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append oldobjs " $func_extract_archives_result" + fi + + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + echo "copying selected object files to avoid basename conflicts..." + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase="$func_basename_result" + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + func_append oldobjs " $gentop/$newobj" + ;; + *) func_append oldobjs " $obj" ;; + esac + done + fi + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + elif test -n "$archiver_list_spec"; then + func_verbose "using command file archive linking..." + for obj in $oldobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > $output_objdir/$libname.libcmd + func_to_tool_file "$output_objdir/$libname.libcmd" + oldobjs=" $archiver_list_spec$func_to_tool_file_result" + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + func_append objlist " $obj" + if test "$len" -lt "$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= + len=$len0 + 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 + func_execute_cmds "$cmds" 'exit $?' + done + + test -n "$generated" && \ + func_show_eval "${RM}r$generated" + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + func_verbose "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}\" || $lt_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 + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; 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 "$relink_command" | $SED "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + + # Only create the output if not a dry run. + $opt_dry_run || { + 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) + func_basename "$deplib" + name="$func_basename_result" + func_resolve_sysroot "$deplib" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" + ;; + -L*) + func_stripname -L '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -L$func_replace_sysroot_result" + ;; + -R*) + func_stripname -R '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -R$func_replace_sysroot_result" + ;; + *) func_append newdependency_libs " $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" + ;; + *) func_append newdlfiles " $lib" ;; + esac + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" + ;; + esac + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlfiles " $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlprefiles " $abs" + done + dlprefiles="$newdlprefiles" + fi + $RM $output + # place dlname in correct position for cygwin + # In fact, it would be nice if we could use this code for all target + # systems that can't hard-code library paths into their executables + # and that have no shared library path variable independent of PATH, + # but it turns out we can't easily determine that from inspecting + # libtool variables, so we have to hard-code the OSs to which it + # applies here; at the moment, that means platforms that use the PE + # object format with DLL files. See the long comment at the top of + # tests/bindir.at for full details. + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) + # If a -bindir argument was supplied, place the dll there. + if test "x$bindir" != x ; + then + func_relative_path "$install_libdir" "$bindir" + tdlname=$func_relative_path_result$dlname + else + # Otherwise fall back on heuristic. + tdlname=../bin/$dlname + fi + ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# 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' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Names of additional weak libraries provided by this library +weak_library_names='$weak_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 + } + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} + +{ test "$opt_mode" = link || test "$opt_mode" = relink; } && + func_mode_link ${1+"$@"} + + +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $opt_debug + 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) func_append RM " $arg"; rmforce=yes ;; + -*) func_append RM " $arg" ;; + *) func_append files " $arg" ;; + esac + done + + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" + + rmdirs= + + for file in $files; do + func_dirname "$file" "" "." + dir="$func_dirname_result" + if test "X$dir" = X.; then + odir="$objdir" + else + odir="$dir/$objdir" + fi + func_basename "$file" + name="$func_basename_result" + test "$opt_mode" = uninstall && odir="$dir" + + # Remember odir for removal later, being careful to avoid duplicates + if test "$opt_mode" = clean; then + case " $rmdirs " in + *" $odir "*) ;; + *) func_append rmdirs " $odir" ;; + 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 func_lalib_p "$file"; then + func_source $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + func_append rmfiles " $odir/$n" + done + test -n "$old_library" && func_append rmfiles " $odir/$old_library" + + case "$opt_mode" in + clean) + case " $library_names " in + *" $dlname "*) ;; + *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; + esac + test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if func_lalib_p "$file"; then + + # Read the .lo file + func_source $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" && + test "$pic_object" != none; then + func_append 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 + func_append rmfiles " $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$opt_mode" = clean ; then + noexename=$name + case $file in + *.exe) + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result + # $file with .exe has already been added to rmfiles, + # add $file without .exe + func_append rmfiles " $file" + ;; + esac + # Do a test to see if this is a libtool program. + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + func_append rmfiles " $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + func_append rmfiles " $odir/$name $odir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + func_append rmfiles " $odir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + func_append rmfiles " $odir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + func_show_eval "$RM $rmfiles" 'exit_status=1' + done + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + func_show_eval "rmdir $dir >/dev/null 2>&1" + fi + done + + exit $exit_status +} + +{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && + func_mode_uninstall ${1+"$@"} + +test -z "$opt_mode" && { + help="$generic_help" + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode \`$opt_mode'" + +if test -n "$exec_cmd"; then + eval exec "$exec_cmd" + exit $EXIT_FAILURE +fi + +exit $exit_status + + +# 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: +# vi:sw=2 + diff --git a/missing b/missing new file mode 100755 index 0000000000000000000000000000000000000000..db98974ff5d59295d7e0edfec2eb2069dc78ef1a --- /dev/null +++ b/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2013-10-28.13; # UTC + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 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, 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 <http://www.gnu.org/licenses/>. + +# 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. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to <bug-automake@gnu.org>." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=http://www.perl.org/ +flex_URL=http://flex.sourceforge.net/ +gnu_software_URL=http://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/ntfsprogs/Android.mk b/ntfsprogs/Android.mk new file mode 100755 index 0000000000000000000000000000000000000000..4c73531a1a971f1cfcf5ef4932330d98153a7b1e --- /dev/null +++ b/ntfsprogs/Android.mk @@ -0,0 +1,47 @@ +LOCAL_PATH := $(call my-dir) + +######################### +# Build the mkntfs binary + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := \ + attrdef.c \ + boot.c \ + sd.c \ + mkntfs.c \ + utils.c + +LOCAL_MODULE := mkntfs +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := libntfs-3g + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../include/ntfs-3g \ + $(LOCAL_PATH)/.. + +LOCAL_CFLAGS := -O2 -W -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H + +include $(BUILD_EXECUTABLE) + +######################### +# Build the ntfsfix binary + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := \ + ntfsfix.c \ + utils.c + +LOCAL_MODULE := ntfsfix +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := libntfs-3g + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../include/ntfs-3g \ + $(LOCAL_PATH)/.. + +LOCAL_CFLAGS := -O2 -g -W -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_CONFIG_H + +include $(BUILD_EXECUTABLE) + diff --git a/ntfsprogs/Makefile b/ntfsprogs/Makefile new file mode 100755 index 0000000000000000000000000000000000000000..561f4bc0d6d8ca134a04030b2dac48cd61af2a20 --- /dev/null +++ b/ntfsprogs/Makefile @@ -0,0 +1,1364 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# ntfsprogs/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + + +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/ntfs-3g +pkgincludedir = $(includedir)/ntfs-3g +pkglibdir = $(libdir)/ntfs-3g +pkglibexecdir = $(libexecdir)/ntfs-3g +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = x86_64-unknown-linux-gnu +host_triplet = x86_64-unknown-linux-gnu +target_triplet = x86_64-unknown-linux-gnu +bin_PROGRAMS = ntfsfix$(EXEEXT) \ + ntfsinfo$(EXEEXT) ntfscluster$(EXEEXT) \ + ntfsls$(EXEEXT) ntfscat$(EXEEXT) \ + ntfscmp$(EXEEXT) $(am__EXEEXT_3) \ + $(am__EXEEXT_5) +sbin_PROGRAMS = mkntfs$(EXEEXT) \ + ntfslabel$(EXEEXT) \ + ntfsundelete$(EXEEXT) \ + ntfsresize$(EXEEXT) ntfsclone$(EXEEXT) \ + ntfscp$(EXEEXT) +#am__append_1 = ntfsdecrypt +#am__append_2 = $(EXTRA_PROGRAM_NAMES) +##am__append_3 = $(QUARANTINED_PROGRAM_NAMES) +EXTRA_PROGRAMS = \ + $(am__EXEEXT_2) +subdir = ntfsprogs +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/mkntfs.8.in $(srcdir)/ntfscat.8.in \ + $(srcdir)/ntfsclone.8.in $(srcdir)/ntfscluster.8.in \ + $(srcdir)/ntfscmp.8.in $(srcdir)/ntfscp.8.in \ + $(srcdir)/ntfsfix.8.in $(srcdir)/ntfsinfo.8.in \ + $(srcdir)/ntfslabel.8.in $(srcdir)/ntfsls.8.in \ + $(srcdir)/ntfsprogs.8.in $(srcdir)/ntfsresize.8.in \ + $(srcdir)/ntfsundelete.8.in $(srcdir)/ntfsdecrypt.8.in \ + $(srcdir)/ntfswipe.8.in $(srcdir)/ntfstruncate.8.in \ + $(srcdir)/ntfsfallocate.8.in $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = mkntfs.8 ntfscat.8 ntfsclone.8 ntfscluster.8 \ + ntfscmp.8 ntfscp.8 ntfsfix.8 ntfsinfo.8 ntfslabel.8 ntfsls.8 \ + ntfsprogs.8 ntfsresize.8 ntfsundelete.8 ntfsdecrypt.8 \ + ntfswipe.8 ntfstruncate.8 ntfsfallocate.8 +CONFIG_CLEAN_VPATH_FILES = +#am__EXEEXT_1 = ntfsdecrypt$(EXEEXT) +am__EXEEXT_2 = ntfswipe$(EXEEXT) \ + ntfstruncate$(EXEEXT) $(am__EXEEXT_1) +#am__EXEEXT_3 = \ +# $(am__EXEEXT_2) +am__EXEEXT_4 = ntfsdump_logfile$(EXEEXT) \ + ntfsmftalloc$(EXEEXT) ntfsmove$(EXEEXT) \ + ntfsck$(EXEEXT) ntfsfallocate$(EXEEXT) +##am__EXEEXT_5 = $(am__EXEEXT_4) +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" \ + "$(DESTDIR)$(man8dir)" +PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS) +am__mkntfs_SOURCES_DIST = attrdef.c attrdef.h boot.c boot.h sd.c sd.h \ + mkntfs.c utils.c utils.h +am_mkntfs_OBJECTS = mkntfs-attrdef.$(OBJEXT) \ + mkntfs-boot.$(OBJEXT) \ + mkntfs-sd.$(OBJEXT) \ + mkntfs-mkntfs.$(OBJEXT) \ + mkntfs-utils.$(OBJEXT) +mkntfs_OBJECTS = $(am_mkntfs_OBJECTS) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = \ + $(top_builddir)/libntfs-3g/libntfs-3g.la +#am__DEPENDENCIES_2 = $(top_builddir)/libntfs-3g/.libs/libntfs-3g.a \ +# $(am__DEPENDENCIES_1) +mkntfs_DEPENDENCIES = $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +am__v_lt_1 = +mkntfs_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mkntfs_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfscat_SOURCES_DIST = ntfscat.c ntfscat.h utils.c utils.h +am_ntfscat_OBJECTS = ntfscat.$(OBJEXT) \ + utils.$(OBJEXT) +ntfscat_OBJECTS = $(am_ntfscat_OBJECTS) +ntfscat_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfscat_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfscat_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsck_SOURCES_DIST = ntfsck.c utils.c utils.h +am_ntfsck_OBJECTS = ntfsck.$(OBJEXT) \ + utils.$(OBJEXT) +ntfsck_OBJECTS = $(am_ntfsck_OBJECTS) +ntfsck_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsck_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsck_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsclone_SOURCES_DIST = ntfsclone.c utils.c utils.h +am_ntfsclone_OBJECTS = ntfsclone.$(OBJEXT) \ + utils.$(OBJEXT) +ntfsclone_OBJECTS = $(am_ntfsclone_OBJECTS) +ntfsclone_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsclone_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsclone_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfscluster_SOURCES_DIST = ntfscluster.c ntfscluster.h cluster.c \ + cluster.h utils.c utils.h +am_ntfscluster_OBJECTS = ntfscluster.$(OBJEXT) \ + cluster.$(OBJEXT) utils.$(OBJEXT) +ntfscluster_OBJECTS = $(am_ntfscluster_OBJECTS) +ntfscluster_DEPENDENCIES = \ + $(am__DEPENDENCIES_2) +ntfscluster_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfscluster_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfscmp_SOURCES_DIST = ntfscmp.c utils.c utils.h +am_ntfscmp_OBJECTS = ntfscmp.$(OBJEXT) \ + utils.$(OBJEXT) +ntfscmp_OBJECTS = $(am_ntfscmp_OBJECTS) +ntfscmp_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfscmp_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfscmp_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfscp_SOURCES_DIST = ntfscp.c utils.c utils.h +am_ntfscp_OBJECTS = ntfscp.$(OBJEXT) \ + utils.$(OBJEXT) +ntfscp_OBJECTS = $(am_ntfscp_OBJECTS) +ntfscp_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfscp_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfscp_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsdecrypt_SOURCES_DIST = ntfsdecrypt.c utils.c utils.h +#am_ntfsdecrypt_OBJECTS = ntfsdecrypt-ntfsdecrypt.$(OBJEXT) \ +# ntfsdecrypt-utils.$(OBJEXT) +ntfsdecrypt_OBJECTS = $(am_ntfsdecrypt_OBJECTS) +#ntfsdecrypt_DEPENDENCIES = $(am__DEPENDENCIES_2) \ +# $(am__DEPENDENCIES_1) \ +# $(am__DEPENDENCIES_1) +ntfsdecrypt_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ntfsdecrypt_CFLAGS) \ + $(CFLAGS) $(ntfsdecrypt_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsdump_logfile_SOURCES_DIST = ntfsdump_logfile.c +am_ntfsdump_logfile_OBJECTS = \ + ntfsdump_logfile.$(OBJEXT) +ntfsdump_logfile_OBJECTS = $(am_ntfsdump_logfile_OBJECTS) +ntfsdump_logfile_DEPENDENCIES = \ + $(am__DEPENDENCIES_2) +ntfsdump_logfile_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(ntfsdump_logfile_LDFLAGS) $(LDFLAGS) \ + -o $@ +am__ntfsfallocate_SOURCES_DIST = ntfsfallocate.c utils.c utils.h +am_ntfsfallocate_OBJECTS = \ + ntfsfallocate.$(OBJEXT) utils.$(OBJEXT) +ntfsfallocate_OBJECTS = $(am_ntfsfallocate_OBJECTS) +ntfsfallocate_DEPENDENCIES = \ + $(am__DEPENDENCIES_2) +ntfsfallocate_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsfallocate_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsfix_SOURCES_DIST = ntfsfix.c utils.c utils.h +am_ntfsfix_OBJECTS = ntfsfix.$(OBJEXT) \ + utils.$(OBJEXT) +ntfsfix_OBJECTS = $(am_ntfsfix_OBJECTS) +ntfsfix_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsfix_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsfix_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsinfo_SOURCES_DIST = ntfsinfo.c utils.c utils.h +am_ntfsinfo_OBJECTS = ntfsinfo.$(OBJEXT) \ + utils.$(OBJEXT) +ntfsinfo_OBJECTS = $(am_ntfsinfo_OBJECTS) +ntfsinfo_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsinfo_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsinfo_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfslabel_SOURCES_DIST = ntfslabel.c utils.c utils.h +am_ntfslabel_OBJECTS = ntfslabel.$(OBJEXT) \ + utils.$(OBJEXT) +ntfslabel_OBJECTS = $(am_ntfslabel_OBJECTS) +ntfslabel_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfslabel_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfslabel_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsls_SOURCES_DIST = ntfsls.c utils.c utils.h list.h +am_ntfsls_OBJECTS = ntfsls.$(OBJEXT) \ + utils.$(OBJEXT) +ntfsls_OBJECTS = $(am_ntfsls_OBJECTS) +ntfsls_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsls_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsls_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsmftalloc_SOURCES_DIST = ntfsmftalloc.c utils.c utils.h +am_ntfsmftalloc_OBJECTS = \ + ntfsmftalloc.$(OBJEXT) utils.$(OBJEXT) +ntfsmftalloc_OBJECTS = $(am_ntfsmftalloc_OBJECTS) +ntfsmftalloc_DEPENDENCIES = \ + $(am__DEPENDENCIES_2) +ntfsmftalloc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsmftalloc_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsmove_SOURCES_DIST = ntfsmove.c ntfsmove.h utils.c utils.h +am_ntfsmove_OBJECTS = ntfsmove.$(OBJEXT) \ + utils.$(OBJEXT) +ntfsmove_OBJECTS = $(am_ntfsmove_OBJECTS) +ntfsmove_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsmove_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsmove_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsresize_SOURCES_DIST = ntfsresize.c utils.c utils.h +am_ntfsresize_OBJECTS = ntfsresize.$(OBJEXT) \ + utils.$(OBJEXT) +ntfsresize_OBJECTS = $(am_ntfsresize_OBJECTS) +ntfsresize_DEPENDENCIES = \ + $(am__DEPENDENCIES_2) +ntfsresize_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsresize_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfstruncate_SOURCES_DIST = attrdef.c ntfstruncate.c utils.c \ + utils.h +am_ntfstruncate_OBJECTS = attrdef.$(OBJEXT) \ + ntfstruncate.$(OBJEXT) utils.$(OBJEXT) +ntfstruncate_OBJECTS = $(am_ntfstruncate_OBJECTS) +ntfstruncate_DEPENDENCIES = \ + $(am__DEPENDENCIES_2) +ntfstruncate_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfstruncate_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsundelete_SOURCES_DIST = ntfsundelete.c ntfsundelete.h utils.c \ + utils.h list.h +am_ntfsundelete_OBJECTS = \ + ntfsundelete.$(OBJEXT) utils.$(OBJEXT) +ntfsundelete_OBJECTS = $(am_ntfsundelete_OBJECTS) +ntfsundelete_DEPENDENCIES = \ + $(am__DEPENDENCIES_2) +ntfsundelete_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsundelete_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfswipe_SOURCES_DIST = ntfswipe.c ntfswipe.h utils.c utils.h +am_ntfswipe_OBJECTS = ntfswipe.$(OBJEXT) \ + utils.$(OBJEXT) +ntfswipe_OBJECTS = $(am_ntfswipe_OBJECTS) +ntfswipe_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfswipe_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfswipe_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_$(V)) +am__v_P_ = $(am__v_P_$(AM_DEFAULT_VERBOSITY)) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I. -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +AM_V_CCLD = $(am__v_CCLD_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(mkntfs_SOURCES) $(ntfscat_SOURCES) $(ntfsck_SOURCES) \ + $(ntfsclone_SOURCES) $(ntfscluster_SOURCES) $(ntfscmp_SOURCES) \ + $(ntfscp_SOURCES) $(ntfsdecrypt_SOURCES) \ + $(ntfsdump_logfile_SOURCES) $(ntfsfallocate_SOURCES) \ + $(ntfsfix_SOURCES) $(ntfsinfo_SOURCES) $(ntfslabel_SOURCES) \ + $(ntfsls_SOURCES) $(ntfsmftalloc_SOURCES) $(ntfsmove_SOURCES) \ + $(ntfsresize_SOURCES) $(ntfstruncate_SOURCES) \ + $(ntfsundelete_SOURCES) $(ntfswipe_SOURCES) +DIST_SOURCES = $(am__mkntfs_SOURCES_DIST) $(am__ntfscat_SOURCES_DIST) \ + $(am__ntfsck_SOURCES_DIST) $(am__ntfsclone_SOURCES_DIST) \ + $(am__ntfscluster_SOURCES_DIST) $(am__ntfscmp_SOURCES_DIST) \ + $(am__ntfscp_SOURCES_DIST) $(am__ntfsdecrypt_SOURCES_DIST) \ + $(am__ntfsdump_logfile_SOURCES_DIST) \ + $(am__ntfsfallocate_SOURCES_DIST) $(am__ntfsfix_SOURCES_DIST) \ + $(am__ntfsinfo_SOURCES_DIST) $(am__ntfslabel_SOURCES_DIST) \ + $(am__ntfsls_SOURCES_DIST) $(am__ntfsmftalloc_SOURCES_DIST) \ + $(am__ntfsmove_SOURCES_DIST) $(am__ntfsresize_SOURCES_DIST) \ + $(am__ntfstruncate_SOURCES_DIST) \ + $(am__ntfsundelete_SOURCES_DIST) $(am__ntfswipe_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(man_MANS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing aclocal-1.14 +AMTAR = $${TAR-tar} +AM_DEFAULT_VERBOSITY = 1 +AR = ar +AUTOCONF = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoconf +AUTOHEADER = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing autoheader +AUTOMAKE = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing automake-1.14 +AWK = gawk +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -g -O2 -Wall +CPP = gcc -E +CPPFLAGS = +CYGPATH_W = echo +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +DLLTOOL = false +DSYMUTIL = +DUMPBIN = +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = /bin/grep -E +EXEEXT = +FGREP = /bin/grep -F +FUSE_MODULE_CFLAGS = +FUSE_MODULE_LIBS = +GNUTLS_CFLAGS = +GNUTLS_LIBS = +GREP = /bin/grep +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = $(install_sh) -c -s +LD = /usr/bin/ld -m elf_x86_64 +LDCONFIG = /sbin/ldconfig +LDFLAGS = +LIBFUSE_LITE_CFLAGS = +LIBFUSE_LITE_LIBS = -lpthread +LIBGCRYPT_CFLAGS = +LIBGCRYPT_CONFIG = +LIBGCRYPT_LIBS = +LIBNTFS_3G_VERSION = 86 +LIBNTFS_CPPFLAGS = +LIBNTFS_LIBS = +LIBOBJS = +LIBS = +LIBTOOL = $(SHELL) $(top_builddir)/libtool +LIPO = +LN_S = ln -s +LTLIBOBJS = +MAINT = # +MAKEINFO = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/missing makeinfo +MANIFEST_TOOL = : +MKDIR_P = /bin/mkdir -p +MKNTFS_CPPFLAGS = +MKNTFS_LIBS = +MV = /bin/mv +NM = /usr/bin/nm -B +NMEDIT = +NTFSPROGS_STATIC_LIBS = +OBJDUMP = objdump +OBJEXT = o +OTOOL = +OTOOL64 = +OUTPUT_FORMAT = +PACKAGE = ntfs-3g +PACKAGE_BUGREPORT = ntfs-3g-devel@lists.sf.net +PACKAGE_NAME = ntfs-3g +PACKAGE_STRING = ntfs-3g 2015.3.14 +PACKAGE_TARNAME = ntfs-3g +PACKAGE_URL = +PACKAGE_VERSION = 2015.3.14 +PATH_SEPARATOR = : +PKG_CONFIG = /usr/bin/pkg-config +PKG_CONFIG_LIBDIR = +PKG_CONFIG_PATH = +RANLIB = ranlib +RM = /bin/rm +SED = /bin/sed +SET_MAKE = +SHELL = /bin/bash +STRIP = strip +VERSION = 2015.3.14 +abs_builddir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/ntfsprogs +abs_srcdir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/ntfsprogs +abs_top_builddir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14 +abs_top_srcdir = /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14 +ac_ct_AR = ar +ac_ct_CC = gcc +ac_ct_DUMPBIN = +all_includes = +all_libraries = +am__include = include +am__leading_dot = . +am__quote = +am__tar = $${TAR-tar} chof - "$$tardir" +am__untar = $${TAR-tar} xf - +bindir = ${exec_prefix}/bin +build = x86_64-unknown-linux-gnu +build_alias = +build_cpu = x86_64 +build_os = linux-gnu +build_vendor = unknown +builddir = . +datadir = ${datarootdir} +datarootdir = ${prefix}/share +docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} +dvidir = ${docdir} +exec_prefix = ${prefix} +host = x86_64-unknown-linux-gnu +host_alias = +host_cpu = x86_64 +host_os = linux-gnu +host_vendor = unknown +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = ${SHELL} /home/cmc/share/rockchip/llolipop_flash/external/ntfs-3g_ntfsprogs-2015.3.14/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = $(MKDIR_P) +ntfs3gincludedir = $(includedir)/ntfs-3g +oldincludedir = /usr/include +pdfdir = ${docdir} +pkgconfigdir = $(libdir)/pkgconfig +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +rootbindir = /bin +rootlibdir = /lib +rootsbindir = /sbin +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +srcdir = . +sysconfdir = ${prefix}/etc +target = x86_64-unknown-linux-gnu +target_alias = +target_cpu = x86_64 +target_os = linux-gnu +target_vendor = unknown +top_build_prefix = ../ +top_builddir = .. +top_srcdir = .. +AM_LIBS = $(top_builddir)/libntfs-3g/libntfs-3g.la +#AM_LIBS = $(top_builddir)/libntfs-3g/.libs/libntfs-3g.a $(NTFSPROGS_STATIC_LIBS) +AM_LFLAGS = $(all_libraries) +# older builds may need -static instead of newer -all-static +#AM_LFLAGS = -static +#STATIC_LINK = $(CC) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +LIBTOOL_LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ + +# Workaround to make REALLYSTATIC work with automake 1.5. +LINK = $(STATIC_LINK) $(LIBTOOL_LINK) +EXTRA_PROGRAM_NAMES = ntfswipe ntfstruncate \ + $(am__append_1) +QUARANTINED_PROGRAM_NAMES = ntfsdump_logfile ntfsmftalloc ntfsmove ntfsck \ + ntfsfallocate + +man_MANS = mkntfs.8 ntfsfix.8 ntfslabel.8 ntfsinfo.8 \ + ntfsundelete.8 ntfsresize.8 ntfsprogs.8 ntfsls.8 \ + ntfsclone.8 ntfscluster.8 ntfscat.8 ntfscp.8 \ + ntfscmp.8 ntfswipe.8 ntfstruncate.8 \ + ntfsdecrypt.8 ntfsfallocate.8 + +EXTRA_MANS = +CLEANFILES = $(EXTRA_PROGRAMS) +MAINTAINERCLEANFILES = Makefile.in + +# Set the include path. +AM_CPPFLAGS = -I$(top_srcdir)/include/ntfs-3g $(all_includes) +ntfsfix_SOURCES = ntfsfix.c utils.c utils.h +ntfsfix_LDADD = $(AM_LIBS) +ntfsfix_LDFLAGS = $(AM_LFLAGS) +mkntfs_CPPFLAGS = $(AM_CPPFLAGS) $(MKNTFS_CPPFLAGS) +mkntfs_SOURCES = attrdef.c attrdef.h boot.c boot.h sd.c sd.h mkntfs.c utils.c utils.h +mkntfs_LDADD = $(AM_LIBS) $(MKNTFS_LIBS) +mkntfs_LDFLAGS = $(AM_LFLAGS) +ntfslabel_SOURCES = ntfslabel.c utils.c utils.h +ntfslabel_LDADD = $(AM_LIBS) +ntfslabel_LDFLAGS = $(AM_LFLAGS) +ntfsinfo_SOURCES = ntfsinfo.c utils.c utils.h +ntfsinfo_LDADD = $(AM_LIBS) +ntfsinfo_LDFLAGS = $(AM_LFLAGS) +ntfsundelete_SOURCES = ntfsundelete.c ntfsundelete.h utils.c utils.h list.h +ntfsundelete_LDADD = $(AM_LIBS) +ntfsundelete_LDFLAGS = $(AM_LFLAGS) +ntfsresize_SOURCES = ntfsresize.c utils.c utils.h +ntfsresize_LDADD = $(AM_LIBS) +ntfsresize_LDFLAGS = $(AM_LFLAGS) +ntfsclone_SOURCES = ntfsclone.c utils.c utils.h +ntfsclone_LDADD = $(AM_LIBS) +ntfsclone_LDFLAGS = $(AM_LFLAGS) +ntfscluster_SOURCES = ntfscluster.c ntfscluster.h cluster.c cluster.h utils.c utils.h +ntfscluster_LDADD = $(AM_LIBS) +ntfscluster_LDFLAGS = $(AM_LFLAGS) +ntfsls_SOURCES = ntfsls.c utils.c utils.h list.h +ntfsls_LDADD = $(AM_LIBS) +ntfsls_LDFLAGS = $(AM_LFLAGS) +ntfscat_SOURCES = ntfscat.c ntfscat.h utils.c utils.h +ntfscat_LDADD = $(AM_LIBS) +ntfscat_LDFLAGS = $(AM_LFLAGS) +ntfscp_SOURCES = ntfscp.c utils.c utils.h +ntfscp_LDADD = $(AM_LIBS) +ntfscp_LDFLAGS = $(AM_LFLAGS) +ntfsck_SOURCES = ntfsck.c utils.c utils.h +ntfsck_LDADD = $(AM_LIBS) +ntfsck_LDFLAGS = $(AM_LFLAGS) +ntfscmp_SOURCES = ntfscmp.c utils.c utils.h +ntfscmp_LDADD = $(AM_LIBS) +ntfscmp_LDFLAGS = $(AM_LFLAGS) + +# We don't distribute these +ntfstruncate_SOURCES = attrdef.c ntfstruncate.c utils.c utils.h +ntfstruncate_LDADD = $(AM_LIBS) +ntfstruncate_LDFLAGS = $(AM_LFLAGS) +ntfsmftalloc_SOURCES = ntfsmftalloc.c utils.c utils.h +ntfsmftalloc_LDADD = $(AM_LIBS) +ntfsmftalloc_LDFLAGS = $(AM_LFLAGS) +ntfsmove_SOURCES = ntfsmove.c ntfsmove.h utils.c utils.h +ntfsmove_LDADD = $(AM_LIBS) +ntfsmove_LDFLAGS = $(AM_LFLAGS) +ntfswipe_SOURCES = ntfswipe.c ntfswipe.h utils.c utils.h +ntfswipe_LDADD = $(AM_LIBS) +ntfswipe_LDFLAGS = $(AM_LFLAGS) +ntfsdump_logfile_SOURCES = ntfsdump_logfile.c +ntfsdump_logfile_LDADD = $(AM_LIBS) +ntfsdump_logfile_LDFLAGS = $(AM_LFLAGS) +ntfsfallocate_SOURCES = ntfsfallocate.c utils.c utils.h +ntfsfallocate_LDADD = $(AM_LIBS) +ntfsfallocate_LDFLAGS = $(AM_LFLAGS) +#ntfsdecrypt_SOURCES = ntfsdecrypt.c utils.c utils.h +#ntfsdecrypt_LDADD = $(AM_LIBS) $(GNUTLS_LIBS) $(LIBGCRYPT_LIBS) +#ntfsdecrypt_LDFLAGS = $(AM_LFLAGS) +#ntfsdecrypt_CFLAGS = $(GNUTLS_CFLAGS) $(LIBGCRYPT_CFLAGS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: # $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu ntfsprogs/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu ntfsprogs/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: # $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): # $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +mkntfs.8: $(top_builddir)/config.status $(srcdir)/mkntfs.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfscat.8: $(top_builddir)/config.status $(srcdir)/ntfscat.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsclone.8: $(top_builddir)/config.status $(srcdir)/ntfsclone.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfscluster.8: $(top_builddir)/config.status $(srcdir)/ntfscluster.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfscmp.8: $(top_builddir)/config.status $(srcdir)/ntfscmp.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfscp.8: $(top_builddir)/config.status $(srcdir)/ntfscp.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsfix.8: $(top_builddir)/config.status $(srcdir)/ntfsfix.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsinfo.8: $(top_builddir)/config.status $(srcdir)/ntfsinfo.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfslabel.8: $(top_builddir)/config.status $(srcdir)/ntfslabel.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsls.8: $(top_builddir)/config.status $(srcdir)/ntfsls.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsprogs.8: $(top_builddir)/config.status $(srcdir)/ntfsprogs.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsresize.8: $(top_builddir)/config.status $(srcdir)/ntfsresize.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsundelete.8: $(top_builddir)/config.status $(srcdir)/ntfsundelete.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsdecrypt.8: $(top_builddir)/config.status $(srcdir)/ntfsdecrypt.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfswipe.8: $(top_builddir)/config.status $(srcdir)/ntfswipe.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfstruncate.8: $(top_builddir)/config.status $(srcdir)/ntfstruncate.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsfallocate.8: $(top_builddir)/config.status $(srcdir)/ntfsfallocate.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +mkntfs$(EXEEXT): $(mkntfs_OBJECTS) $(mkntfs_DEPENDENCIES) $(EXTRA_mkntfs_DEPENDENCIES) + @rm -f mkntfs$(EXEEXT) + $(AM_V_CCLD)$(mkntfs_LINK) $(mkntfs_OBJECTS) $(mkntfs_LDADD) $(LIBS) + +ntfscat$(EXEEXT): $(ntfscat_OBJECTS) $(ntfscat_DEPENDENCIES) $(EXTRA_ntfscat_DEPENDENCIES) + @rm -f ntfscat$(EXEEXT) + $(AM_V_CCLD)$(ntfscat_LINK) $(ntfscat_OBJECTS) $(ntfscat_LDADD) $(LIBS) + +ntfsck$(EXEEXT): $(ntfsck_OBJECTS) $(ntfsck_DEPENDENCIES) $(EXTRA_ntfsck_DEPENDENCIES) + @rm -f ntfsck$(EXEEXT) + $(AM_V_CCLD)$(ntfsck_LINK) $(ntfsck_OBJECTS) $(ntfsck_LDADD) $(LIBS) + +ntfsclone$(EXEEXT): $(ntfsclone_OBJECTS) $(ntfsclone_DEPENDENCIES) $(EXTRA_ntfsclone_DEPENDENCIES) + @rm -f ntfsclone$(EXEEXT) + $(AM_V_CCLD)$(ntfsclone_LINK) $(ntfsclone_OBJECTS) $(ntfsclone_LDADD) $(LIBS) + +ntfscluster$(EXEEXT): $(ntfscluster_OBJECTS) $(ntfscluster_DEPENDENCIES) $(EXTRA_ntfscluster_DEPENDENCIES) + @rm -f ntfscluster$(EXEEXT) + $(AM_V_CCLD)$(ntfscluster_LINK) $(ntfscluster_OBJECTS) $(ntfscluster_LDADD) $(LIBS) + +ntfscmp$(EXEEXT): $(ntfscmp_OBJECTS) $(ntfscmp_DEPENDENCIES) $(EXTRA_ntfscmp_DEPENDENCIES) + @rm -f ntfscmp$(EXEEXT) + $(AM_V_CCLD)$(ntfscmp_LINK) $(ntfscmp_OBJECTS) $(ntfscmp_LDADD) $(LIBS) + +ntfscp$(EXEEXT): $(ntfscp_OBJECTS) $(ntfscp_DEPENDENCIES) $(EXTRA_ntfscp_DEPENDENCIES) + @rm -f ntfscp$(EXEEXT) + $(AM_V_CCLD)$(ntfscp_LINK) $(ntfscp_OBJECTS) $(ntfscp_LDADD) $(LIBS) + +ntfsdecrypt$(EXEEXT): $(ntfsdecrypt_OBJECTS) $(ntfsdecrypt_DEPENDENCIES) $(EXTRA_ntfsdecrypt_DEPENDENCIES) + @rm -f ntfsdecrypt$(EXEEXT) + $(AM_V_CCLD)$(ntfsdecrypt_LINK) $(ntfsdecrypt_OBJECTS) $(ntfsdecrypt_LDADD) $(LIBS) + +ntfsdump_logfile$(EXEEXT): $(ntfsdump_logfile_OBJECTS) $(ntfsdump_logfile_DEPENDENCIES) $(EXTRA_ntfsdump_logfile_DEPENDENCIES) + @rm -f ntfsdump_logfile$(EXEEXT) + $(AM_V_CCLD)$(ntfsdump_logfile_LINK) $(ntfsdump_logfile_OBJECTS) $(ntfsdump_logfile_LDADD) $(LIBS) + +ntfsfallocate$(EXEEXT): $(ntfsfallocate_OBJECTS) $(ntfsfallocate_DEPENDENCIES) $(EXTRA_ntfsfallocate_DEPENDENCIES) + @rm -f ntfsfallocate$(EXEEXT) + $(AM_V_CCLD)$(ntfsfallocate_LINK) $(ntfsfallocate_OBJECTS) $(ntfsfallocate_LDADD) $(LIBS) + +ntfsfix$(EXEEXT): $(ntfsfix_OBJECTS) $(ntfsfix_DEPENDENCIES) $(EXTRA_ntfsfix_DEPENDENCIES) + @rm -f ntfsfix$(EXEEXT) + $(AM_V_CCLD)$(ntfsfix_LINK) $(ntfsfix_OBJECTS) $(ntfsfix_LDADD) $(LIBS) + +ntfsinfo$(EXEEXT): $(ntfsinfo_OBJECTS) $(ntfsinfo_DEPENDENCIES) $(EXTRA_ntfsinfo_DEPENDENCIES) + @rm -f ntfsinfo$(EXEEXT) + $(AM_V_CCLD)$(ntfsinfo_LINK) $(ntfsinfo_OBJECTS) $(ntfsinfo_LDADD) $(LIBS) + +ntfslabel$(EXEEXT): $(ntfslabel_OBJECTS) $(ntfslabel_DEPENDENCIES) $(EXTRA_ntfslabel_DEPENDENCIES) + @rm -f ntfslabel$(EXEEXT) + $(AM_V_CCLD)$(ntfslabel_LINK) $(ntfslabel_OBJECTS) $(ntfslabel_LDADD) $(LIBS) + +ntfsls$(EXEEXT): $(ntfsls_OBJECTS) $(ntfsls_DEPENDENCIES) $(EXTRA_ntfsls_DEPENDENCIES) + @rm -f ntfsls$(EXEEXT) + $(AM_V_CCLD)$(ntfsls_LINK) $(ntfsls_OBJECTS) $(ntfsls_LDADD) $(LIBS) + +ntfsmftalloc$(EXEEXT): $(ntfsmftalloc_OBJECTS) $(ntfsmftalloc_DEPENDENCIES) $(EXTRA_ntfsmftalloc_DEPENDENCIES) + @rm -f ntfsmftalloc$(EXEEXT) + $(AM_V_CCLD)$(ntfsmftalloc_LINK) $(ntfsmftalloc_OBJECTS) $(ntfsmftalloc_LDADD) $(LIBS) + +ntfsmove$(EXEEXT): $(ntfsmove_OBJECTS) $(ntfsmove_DEPENDENCIES) $(EXTRA_ntfsmove_DEPENDENCIES) + @rm -f ntfsmove$(EXEEXT) + $(AM_V_CCLD)$(ntfsmove_LINK) $(ntfsmove_OBJECTS) $(ntfsmove_LDADD) $(LIBS) + +ntfsresize$(EXEEXT): $(ntfsresize_OBJECTS) $(ntfsresize_DEPENDENCIES) $(EXTRA_ntfsresize_DEPENDENCIES) + @rm -f ntfsresize$(EXEEXT) + $(AM_V_CCLD)$(ntfsresize_LINK) $(ntfsresize_OBJECTS) $(ntfsresize_LDADD) $(LIBS) + +ntfstruncate$(EXEEXT): $(ntfstruncate_OBJECTS) $(ntfstruncate_DEPENDENCIES) $(EXTRA_ntfstruncate_DEPENDENCIES) + @rm -f ntfstruncate$(EXEEXT) + $(AM_V_CCLD)$(ntfstruncate_LINK) $(ntfstruncate_OBJECTS) $(ntfstruncate_LDADD) $(LIBS) + +ntfsundelete$(EXEEXT): $(ntfsundelete_OBJECTS) $(ntfsundelete_DEPENDENCIES) $(EXTRA_ntfsundelete_DEPENDENCIES) + @rm -f ntfsundelete$(EXEEXT) + $(AM_V_CCLD)$(ntfsundelete_LINK) $(ntfsundelete_OBJECTS) $(ntfsundelete_LDADD) $(LIBS) + +ntfswipe$(EXEEXT): $(ntfswipe_OBJECTS) $(ntfswipe_DEPENDENCIES) $(EXTRA_ntfswipe_DEPENDENCIES) + @rm -f ntfswipe$(EXEEXT) + $(AM_V_CCLD)$(ntfswipe_LINK) $(ntfswipe_OBJECTS) $(ntfswipe_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +include ./$(DEPDIR)/attrdef.Po +include ./$(DEPDIR)/cluster.Po +include ./$(DEPDIR)/mkntfs-attrdef.Po +include ./$(DEPDIR)/mkntfs-boot.Po +include ./$(DEPDIR)/mkntfs-mkntfs.Po +include ./$(DEPDIR)/mkntfs-sd.Po +include ./$(DEPDIR)/mkntfs-utils.Po +include ./$(DEPDIR)/ntfscat.Po +include ./$(DEPDIR)/ntfsck.Po +include ./$(DEPDIR)/ntfsclone.Po +include ./$(DEPDIR)/ntfscluster.Po +include ./$(DEPDIR)/ntfscmp.Po +include ./$(DEPDIR)/ntfscp.Po +include ./$(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Po +include ./$(DEPDIR)/ntfsdecrypt-utils.Po +include ./$(DEPDIR)/ntfsdump_logfile.Po +include ./$(DEPDIR)/ntfsfallocate.Po +include ./$(DEPDIR)/ntfsfix.Po +include ./$(DEPDIR)/ntfsinfo.Po +include ./$(DEPDIR)/ntfslabel.Po +include ./$(DEPDIR)/ntfsls.Po +include ./$(DEPDIR)/ntfsmftalloc.Po +include ./$(DEPDIR)/ntfsmove.Po +include ./$(DEPDIR)/ntfsresize.Po +include ./$(DEPDIR)/ntfstruncate.Po +include ./$(DEPDIR)/ntfsundelete.Po +include ./$(DEPDIR)/ntfswipe.Po +include ./$(DEPDIR)/utils.Po + +.c.o: + $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< + $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# $(AM_V_CC)source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(COMPILE) -c -o $@ $< + +.c.obj: + $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` + $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# $(AM_V_CC)source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: + $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< + $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +# $(AM_V_CC)source='$<' object='$@' libtool=yes \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(LTCOMPILE) -c -o $@ $< + +mkntfs-attrdef.o: attrdef.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-attrdef.o -MD -MP -MF $(DEPDIR)/mkntfs-attrdef.Tpo -c -o mkntfs-attrdef.o `test -f 'attrdef.c' || echo '$(srcdir)/'`attrdef.c + $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-attrdef.Tpo $(DEPDIR)/mkntfs-attrdef.Po +# $(AM_V_CC)source='attrdef.c' object='mkntfs-attrdef.o' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-attrdef.o `test -f 'attrdef.c' || echo '$(srcdir)/'`attrdef.c + +mkntfs-attrdef.obj: attrdef.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-attrdef.obj -MD -MP -MF $(DEPDIR)/mkntfs-attrdef.Tpo -c -o mkntfs-attrdef.obj `if test -f 'attrdef.c'; then $(CYGPATH_W) 'attrdef.c'; else $(CYGPATH_W) '$(srcdir)/attrdef.c'; fi` + $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-attrdef.Tpo $(DEPDIR)/mkntfs-attrdef.Po +# $(AM_V_CC)source='attrdef.c' object='mkntfs-attrdef.obj' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-attrdef.obj `if test -f 'attrdef.c'; then $(CYGPATH_W) 'attrdef.c'; else $(CYGPATH_W) '$(srcdir)/attrdef.c'; fi` + +mkntfs-boot.o: boot.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-boot.o -MD -MP -MF $(DEPDIR)/mkntfs-boot.Tpo -c -o mkntfs-boot.o `test -f 'boot.c' || echo '$(srcdir)/'`boot.c + $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-boot.Tpo $(DEPDIR)/mkntfs-boot.Po +# $(AM_V_CC)source='boot.c' object='mkntfs-boot.o' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-boot.o `test -f 'boot.c' || echo '$(srcdir)/'`boot.c + +mkntfs-boot.obj: boot.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-boot.obj -MD -MP -MF $(DEPDIR)/mkntfs-boot.Tpo -c -o mkntfs-boot.obj `if test -f 'boot.c'; then $(CYGPATH_W) 'boot.c'; else $(CYGPATH_W) '$(srcdir)/boot.c'; fi` + $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-boot.Tpo $(DEPDIR)/mkntfs-boot.Po +# $(AM_V_CC)source='boot.c' object='mkntfs-boot.obj' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-boot.obj `if test -f 'boot.c'; then $(CYGPATH_W) 'boot.c'; else $(CYGPATH_W) '$(srcdir)/boot.c'; fi` + +mkntfs-sd.o: sd.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-sd.o -MD -MP -MF $(DEPDIR)/mkntfs-sd.Tpo -c -o mkntfs-sd.o `test -f 'sd.c' || echo '$(srcdir)/'`sd.c + $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-sd.Tpo $(DEPDIR)/mkntfs-sd.Po +# $(AM_V_CC)source='sd.c' object='mkntfs-sd.o' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-sd.o `test -f 'sd.c' || echo '$(srcdir)/'`sd.c + +mkntfs-sd.obj: sd.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-sd.obj -MD -MP -MF $(DEPDIR)/mkntfs-sd.Tpo -c -o mkntfs-sd.obj `if test -f 'sd.c'; then $(CYGPATH_W) 'sd.c'; else $(CYGPATH_W) '$(srcdir)/sd.c'; fi` + $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-sd.Tpo $(DEPDIR)/mkntfs-sd.Po +# $(AM_V_CC)source='sd.c' object='mkntfs-sd.obj' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-sd.obj `if test -f 'sd.c'; then $(CYGPATH_W) 'sd.c'; else $(CYGPATH_W) '$(srcdir)/sd.c'; fi` + +mkntfs-mkntfs.o: mkntfs.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-mkntfs.o -MD -MP -MF $(DEPDIR)/mkntfs-mkntfs.Tpo -c -o mkntfs-mkntfs.o `test -f 'mkntfs.c' || echo '$(srcdir)/'`mkntfs.c + $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-mkntfs.Tpo $(DEPDIR)/mkntfs-mkntfs.Po +# $(AM_V_CC)source='mkntfs.c' object='mkntfs-mkntfs.o' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-mkntfs.o `test -f 'mkntfs.c' || echo '$(srcdir)/'`mkntfs.c + +mkntfs-mkntfs.obj: mkntfs.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-mkntfs.obj -MD -MP -MF $(DEPDIR)/mkntfs-mkntfs.Tpo -c -o mkntfs-mkntfs.obj `if test -f 'mkntfs.c'; then $(CYGPATH_W) 'mkntfs.c'; else $(CYGPATH_W) '$(srcdir)/mkntfs.c'; fi` + $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-mkntfs.Tpo $(DEPDIR)/mkntfs-mkntfs.Po +# $(AM_V_CC)source='mkntfs.c' object='mkntfs-mkntfs.obj' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-mkntfs.obj `if test -f 'mkntfs.c'; then $(CYGPATH_W) 'mkntfs.c'; else $(CYGPATH_W) '$(srcdir)/mkntfs.c'; fi` + +mkntfs-utils.o: utils.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-utils.o -MD -MP -MF $(DEPDIR)/mkntfs-utils.Tpo -c -o mkntfs-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c + $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-utils.Tpo $(DEPDIR)/mkntfs-utils.Po +# $(AM_V_CC)source='utils.c' object='mkntfs-utils.o' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c + +mkntfs-utils.obj: utils.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-utils.obj -MD -MP -MF $(DEPDIR)/mkntfs-utils.Tpo -c -o mkntfs-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` + $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-utils.Tpo $(DEPDIR)/mkntfs-utils.Po +# $(AM_V_CC)source='utils.c' object='mkntfs-utils.obj' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` + +ntfsdecrypt-ntfsdecrypt.o: ntfsdecrypt.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -MT ntfsdecrypt-ntfsdecrypt.o -MD -MP -MF $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Tpo -c -o ntfsdecrypt-ntfsdecrypt.o `test -f 'ntfsdecrypt.c' || echo '$(srcdir)/'`ntfsdecrypt.c + $(AM_V_at)$(am__mv) $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Tpo $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Po +# $(AM_V_CC)source='ntfsdecrypt.c' object='ntfsdecrypt-ntfsdecrypt.o' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -c -o ntfsdecrypt-ntfsdecrypt.o `test -f 'ntfsdecrypt.c' || echo '$(srcdir)/'`ntfsdecrypt.c + +ntfsdecrypt-ntfsdecrypt.obj: ntfsdecrypt.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -MT ntfsdecrypt-ntfsdecrypt.obj -MD -MP -MF $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Tpo -c -o ntfsdecrypt-ntfsdecrypt.obj `if test -f 'ntfsdecrypt.c'; then $(CYGPATH_W) 'ntfsdecrypt.c'; else $(CYGPATH_W) '$(srcdir)/ntfsdecrypt.c'; fi` + $(AM_V_at)$(am__mv) $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Tpo $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Po +# $(AM_V_CC)source='ntfsdecrypt.c' object='ntfsdecrypt-ntfsdecrypt.obj' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -c -o ntfsdecrypt-ntfsdecrypt.obj `if test -f 'ntfsdecrypt.c'; then $(CYGPATH_W) 'ntfsdecrypt.c'; else $(CYGPATH_W) '$(srcdir)/ntfsdecrypt.c'; fi` + +ntfsdecrypt-utils.o: utils.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -MT ntfsdecrypt-utils.o -MD -MP -MF $(DEPDIR)/ntfsdecrypt-utils.Tpo -c -o ntfsdecrypt-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c + $(AM_V_at)$(am__mv) $(DEPDIR)/ntfsdecrypt-utils.Tpo $(DEPDIR)/ntfsdecrypt-utils.Po +# $(AM_V_CC)source='utils.c' object='ntfsdecrypt-utils.o' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -c -o ntfsdecrypt-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c + +ntfsdecrypt-utils.obj: utils.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -MT ntfsdecrypt-utils.obj -MD -MP -MF $(DEPDIR)/ntfsdecrypt-utils.Tpo -c -o ntfsdecrypt-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` + $(AM_V_at)$(am__mv) $(DEPDIR)/ntfsdecrypt-utils.Tpo $(DEPDIR)/ntfsdecrypt-utils.Po +# $(AM_V_CC)source='utils.c' object='ntfsdecrypt-utils.obj' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(AM_V_CC_no)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -c -o ntfsdecrypt-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man8: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man8dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.8[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ + done; } + +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man8dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +#uninstall-local: +#install-exec-hook: +#install-data-hook: +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool \ + clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-man + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-sbinPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man8 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-local uninstall-man \ + uninstall-sbinPROGRAMS + +uninstall-man: uninstall-man8 + +.MAKE: install-am install-data-am install-exec-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool \ + clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-data-hook install-dvi install-dvi-am \ + install-exec install-exec-am install-exec-hook install-html \ + install-html-am install-info install-info-am install-man \ + install-man8 install-pdf install-pdf-am install-ps \ + install-ps-am install-sbinPROGRAMS install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ + uninstall-local uninstall-man uninstall-man8 \ + uninstall-sbinPROGRAMS + + +# Extra targets + +strip: $(bin_PROGRAMS) $(sbin_PROGRAMS) + $(STRIP) $^ + +libs: + (cd ../libntfs-3g && $(MAKE) libs) || exit 1; + +extra: extras + +extras: libs $(EXTRA_PROGRAMS) + +# mkfs.ntfs[.8] hard link + +install-exec-hook: + $(INSTALL) -d $(DESTDIR)/sbin + $(LN_S) -f $(sbindir)/mkntfs $(DESTDIR)/sbin/mkfs.ntfs + +install-data-hook: + $(INSTALL) -d $(DESTDIR)$(man8dir) + $(LN_S) -f mkntfs.8 $(DESTDIR)$(man8dir)/mkfs.ntfs.8 + +uninstall-local: + $(RM) -f $(DESTDIR)/sbin/mkfs.ntfs + $(RM) -f $(DESTDIR)$(man8dir)/mkfs.ntfs.8 + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ntfsprogs/Makefile.am b/ntfsprogs/Makefile.am new file mode 100755 index 0000000000000000000000000000000000000000..cbc7ce50dba220da0bbfb6554072d0dabc8b13e1 --- /dev/null +++ b/ntfsprogs/Makefile.am @@ -0,0 +1,164 @@ +if REALLYSTATIC +AM_LIBS = $(top_builddir)/libntfs-3g/.libs/libntfs-3g.a $(NTFSPROGS_STATIC_LIBS) +# older builds may need -static instead of newer -all-static +AM_LFLAGS = -static +STATIC_LINK = $(CC) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +else +AM_LIBS = $(top_builddir)/libntfs-3g/libntfs-3g.la +AM_LFLAGS = $(all_libraries) +LIBTOOL_LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +endif + +# Workaround to make REALLYSTATIC work with automake 1.5. +LINK=$(STATIC_LINK) $(LIBTOOL_LINK) + +if ENABLE_NTFSPROGS + +bin_PROGRAMS = ntfsfix ntfsinfo ntfscluster ntfsls ntfscat ntfscmp +sbin_PROGRAMS = mkntfs ntfslabel ntfsundelete ntfsresize ntfsclone \ + ntfscp +EXTRA_PROGRAM_NAMES = ntfswipe ntfstruncate + +QUARANTINED_PROGRAM_NAMES = ntfsdump_logfile ntfsmftalloc ntfsmove ntfsck \ + ntfsfallocate + +man_MANS = mkntfs.8 ntfsfix.8 ntfslabel.8 ntfsinfo.8 \ + ntfsundelete.8 ntfsresize.8 ntfsprogs.8 ntfsls.8 \ + ntfsclone.8 ntfscluster.8 ntfscat.8 ntfscp.8 \ + ntfscmp.8 ntfswipe.8 ntfstruncate.8 \ + ntfsdecrypt.8 ntfsfallocate.8 +EXTRA_MANS = + +CLEANFILES = $(EXTRA_PROGRAMS) + +MAINTAINERCLEANFILES = Makefile.in + +if ENABLE_CRYPTO +EXTRA_PROGRAM_NAMES += ntfsdecrypt +endif + +if ENABLE_EXTRAS +bin_PROGRAMS += $(EXTRA_PROGRAM_NAMES) +if ENABLE_QUARANTINED +bin_PROGRAMS += $(QUARANTINED_PROGRAM_NAMES) +endif +else +EXTRA_PROGRAMS = $(EXTRA_PROGRAM_NAMES) +endif + +# Set the include path. +AM_CPPFLAGS = -I$(top_srcdir)/include/ntfs-3g $(all_includes) + +ntfsfix_SOURCES = ntfsfix.c utils.c utils.h +ntfsfix_LDADD = $(AM_LIBS) +ntfsfix_LDFLAGS = $(AM_LFLAGS) + +mkntfs_CPPFLAGS = $(AM_CPPFLAGS) $(MKNTFS_CPPFLAGS) +mkntfs_SOURCES = attrdef.c attrdef.h boot.c boot.h sd.c sd.h mkntfs.c utils.c utils.h +mkntfs_LDADD = $(AM_LIBS) $(MKNTFS_LIBS) +mkntfs_LDFLAGS = $(AM_LFLAGS) + +ntfslabel_SOURCES = ntfslabel.c utils.c utils.h +ntfslabel_LDADD = $(AM_LIBS) +ntfslabel_LDFLAGS = $(AM_LFLAGS) + +ntfsinfo_SOURCES = ntfsinfo.c utils.c utils.h +ntfsinfo_LDADD = $(AM_LIBS) +ntfsinfo_LDFLAGS = $(AM_LFLAGS) + +ntfsundelete_SOURCES = ntfsundelete.c ntfsundelete.h utils.c utils.h list.h +ntfsundelete_LDADD = $(AM_LIBS) +ntfsundelete_LDFLAGS = $(AM_LFLAGS) + +ntfsresize_SOURCES = ntfsresize.c utils.c utils.h +ntfsresize_LDADD = $(AM_LIBS) +ntfsresize_LDFLAGS = $(AM_LFLAGS) + +ntfsclone_SOURCES = ntfsclone.c utils.c utils.h +ntfsclone_LDADD = $(AM_LIBS) +ntfsclone_LDFLAGS = $(AM_LFLAGS) + +ntfscluster_SOURCES = ntfscluster.c ntfscluster.h cluster.c cluster.h utils.c utils.h +ntfscluster_LDADD = $(AM_LIBS) +ntfscluster_LDFLAGS = $(AM_LFLAGS) + +ntfsls_SOURCES = ntfsls.c utils.c utils.h list.h +ntfsls_LDADD = $(AM_LIBS) +ntfsls_LDFLAGS = $(AM_LFLAGS) + +ntfscat_SOURCES = ntfscat.c ntfscat.h utils.c utils.h +ntfscat_LDADD = $(AM_LIBS) +ntfscat_LDFLAGS = $(AM_LFLAGS) + +ntfscp_SOURCES = ntfscp.c utils.c utils.h +ntfscp_LDADD = $(AM_LIBS) +ntfscp_LDFLAGS = $(AM_LFLAGS) + +ntfsck_SOURCES = ntfsck.c utils.c utils.h +ntfsck_LDADD = $(AM_LIBS) +ntfsck_LDFLAGS = $(AM_LFLAGS) + +ntfscmp_SOURCES = ntfscmp.c utils.c utils.h +ntfscmp_LDADD = $(AM_LIBS) +ntfscmp_LDFLAGS = $(AM_LFLAGS) + +# We don't distribute these + +ntfstruncate_SOURCES = attrdef.c ntfstruncate.c utils.c utils.h +ntfstruncate_LDADD = $(AM_LIBS) +ntfstruncate_LDFLAGS = $(AM_LFLAGS) + +ntfsmftalloc_SOURCES = ntfsmftalloc.c utils.c utils.h +ntfsmftalloc_LDADD = $(AM_LIBS) +ntfsmftalloc_LDFLAGS = $(AM_LFLAGS) + +ntfsmove_SOURCES = ntfsmove.c ntfsmove.h utils.c utils.h +ntfsmove_LDADD = $(AM_LIBS) +ntfsmove_LDFLAGS = $(AM_LFLAGS) + +ntfswipe_SOURCES = ntfswipe.c ntfswipe.h utils.c utils.h +ntfswipe_LDADD = $(AM_LIBS) +ntfswipe_LDFLAGS = $(AM_LFLAGS) + +ntfsdump_logfile_SOURCES= ntfsdump_logfile.c +ntfsdump_logfile_LDADD = $(AM_LIBS) +ntfsdump_logfile_LDFLAGS= $(AM_LFLAGS) + +ntfsfallocate_SOURCES = ntfsfallocate.c utils.c utils.h +ntfsfallocate_LDADD = $(AM_LIBS) +ntfsfallocate_LDFLAGS = $(AM_LFLAGS) + +if ENABLE_CRYPTO +ntfsdecrypt_SOURCES = ntfsdecrypt.c utils.c utils.h +ntfsdecrypt_LDADD = $(AM_LIBS) $(GNUTLS_LIBS) $(LIBGCRYPT_LIBS) +ntfsdecrypt_LDFLAGS = $(AM_LFLAGS) +ntfsdecrypt_CFLAGS = $(GNUTLS_CFLAGS) $(LIBGCRYPT_CFLAGS) +endif + +# Extra targets + +strip: $(bin_PROGRAMS) $(sbin_PROGRAMS) + $(STRIP) $^ + +libs: + (cd ../libntfs-3g && $(MAKE) libs) || exit 1; + +extra: extras + +extras: libs $(EXTRA_PROGRAMS) + +# mkfs.ntfs[.8] hard link + +install-exec-hook: + $(INSTALL) -d $(DESTDIR)/sbin + $(LN_S) -f $(sbindir)/mkntfs $(DESTDIR)/sbin/mkfs.ntfs + +install-data-hook: + $(INSTALL) -d $(DESTDIR)$(man8dir) + $(LN_S) -f mkntfs.8 $(DESTDIR)$(man8dir)/mkfs.ntfs.8 + +uninstall-local: + $(RM) -f $(DESTDIR)/sbin/mkfs.ntfs + $(RM) -f $(DESTDIR)$(man8dir)/mkfs.ntfs.8 + +endif diff --git a/ntfsprogs/Makefile.in b/ntfsprogs/Makefile.in new file mode 100755 index 0000000000000000000000000000000000000000..8ab3ad734a01ac7e7e144d8ff0296988a24b4457 --- /dev/null +++ b/ntfsprogs/Makefile.in @@ -0,0 +1,1364 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@ENABLE_NTFSPROGS_TRUE@bin_PROGRAMS = ntfsfix$(EXEEXT) \ +@ENABLE_NTFSPROGS_TRUE@ ntfsinfo$(EXEEXT) ntfscluster$(EXEEXT) \ +@ENABLE_NTFSPROGS_TRUE@ ntfsls$(EXEEXT) ntfscat$(EXEEXT) \ +@ENABLE_NTFSPROGS_TRUE@ ntfscmp$(EXEEXT) $(am__EXEEXT_3) \ +@ENABLE_NTFSPROGS_TRUE@ $(am__EXEEXT_5) +@ENABLE_NTFSPROGS_TRUE@sbin_PROGRAMS = mkntfs$(EXEEXT) \ +@ENABLE_NTFSPROGS_TRUE@ ntfslabel$(EXEEXT) \ +@ENABLE_NTFSPROGS_TRUE@ ntfsundelete$(EXEEXT) \ +@ENABLE_NTFSPROGS_TRUE@ ntfsresize$(EXEEXT) ntfsclone$(EXEEXT) \ +@ENABLE_NTFSPROGS_TRUE@ ntfscp$(EXEEXT) +@ENABLE_CRYPTO_TRUE@@ENABLE_NTFSPROGS_TRUE@am__append_1 = ntfsdecrypt +@ENABLE_EXTRAS_TRUE@@ENABLE_NTFSPROGS_TRUE@am__append_2 = $(EXTRA_PROGRAM_NAMES) +@ENABLE_EXTRAS_TRUE@@ENABLE_NTFSPROGS_TRUE@@ENABLE_QUARANTINED_TRUE@am__append_3 = $(QUARANTINED_PROGRAM_NAMES) +@ENABLE_EXTRAS_FALSE@@ENABLE_NTFSPROGS_TRUE@EXTRA_PROGRAMS = \ +@ENABLE_EXTRAS_FALSE@@ENABLE_NTFSPROGS_TRUE@ $(am__EXEEXT_2) +subdir = ntfsprogs +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/mkntfs.8.in $(srcdir)/ntfscat.8.in \ + $(srcdir)/ntfsclone.8.in $(srcdir)/ntfscluster.8.in \ + $(srcdir)/ntfscmp.8.in $(srcdir)/ntfscp.8.in \ + $(srcdir)/ntfsfix.8.in $(srcdir)/ntfsinfo.8.in \ + $(srcdir)/ntfslabel.8.in $(srcdir)/ntfsls.8.in \ + $(srcdir)/ntfsprogs.8.in $(srcdir)/ntfsresize.8.in \ + $(srcdir)/ntfsundelete.8.in $(srcdir)/ntfsdecrypt.8.in \ + $(srcdir)/ntfswipe.8.in $(srcdir)/ntfstruncate.8.in \ + $(srcdir)/ntfsfallocate.8.in $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = mkntfs.8 ntfscat.8 ntfsclone.8 ntfscluster.8 \ + ntfscmp.8 ntfscp.8 ntfsfix.8 ntfsinfo.8 ntfslabel.8 ntfsls.8 \ + ntfsprogs.8 ntfsresize.8 ntfsundelete.8 ntfsdecrypt.8 \ + ntfswipe.8 ntfstruncate.8 ntfsfallocate.8 +CONFIG_CLEAN_VPATH_FILES = +@ENABLE_CRYPTO_TRUE@@ENABLE_NTFSPROGS_TRUE@am__EXEEXT_1 = ntfsdecrypt$(EXEEXT) +@ENABLE_NTFSPROGS_TRUE@am__EXEEXT_2 = ntfswipe$(EXEEXT) \ +@ENABLE_NTFSPROGS_TRUE@ ntfstruncate$(EXEEXT) $(am__EXEEXT_1) +@ENABLE_EXTRAS_TRUE@@ENABLE_NTFSPROGS_TRUE@am__EXEEXT_3 = \ +@ENABLE_EXTRAS_TRUE@@ENABLE_NTFSPROGS_TRUE@ $(am__EXEEXT_2) +@ENABLE_NTFSPROGS_TRUE@am__EXEEXT_4 = ntfsdump_logfile$(EXEEXT) \ +@ENABLE_NTFSPROGS_TRUE@ ntfsmftalloc$(EXEEXT) ntfsmove$(EXEEXT) \ +@ENABLE_NTFSPROGS_TRUE@ ntfsck$(EXEEXT) ntfsfallocate$(EXEEXT) +@ENABLE_EXTRAS_TRUE@@ENABLE_NTFSPROGS_TRUE@@ENABLE_QUARANTINED_TRUE@am__EXEEXT_5 = $(am__EXEEXT_4) +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" \ + "$(DESTDIR)$(man8dir)" +PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS) +am__mkntfs_SOURCES_DIST = attrdef.c attrdef.h boot.c boot.h sd.c sd.h \ + mkntfs.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_mkntfs_OBJECTS = mkntfs-attrdef.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ mkntfs-boot.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ mkntfs-sd.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ mkntfs-mkntfs.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ mkntfs-utils.$(OBJEXT) +mkntfs_OBJECTS = $(am_mkntfs_OBJECTS) +am__DEPENDENCIES_1 = +@REALLYSTATIC_FALSE@am__DEPENDENCIES_2 = \ +@REALLYSTATIC_FALSE@ $(top_builddir)/libntfs-3g/libntfs-3g.la +@REALLYSTATIC_TRUE@am__DEPENDENCIES_2 = $(top_builddir)/libntfs-3g/.libs/libntfs-3g.a \ +@REALLYSTATIC_TRUE@ $(am__DEPENDENCIES_1) +@ENABLE_NTFSPROGS_TRUE@mkntfs_DEPENDENCIES = $(am__DEPENDENCIES_2) \ +@ENABLE_NTFSPROGS_TRUE@ $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +mkntfs_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mkntfs_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfscat_SOURCES_DIST = ntfscat.c ntfscat.h utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfscat_OBJECTS = ntfscat.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfscat_OBJECTS = $(am_ntfscat_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfscat_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfscat_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfscat_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsck_SOURCES_DIST = ntfsck.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfsck_OBJECTS = ntfsck.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfsck_OBJECTS = $(am_ntfsck_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfsck_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsck_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsck_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsclone_SOURCES_DIST = ntfsclone.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfsclone_OBJECTS = ntfsclone.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfsclone_OBJECTS = $(am_ntfsclone_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfsclone_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsclone_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsclone_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfscluster_SOURCES_DIST = ntfscluster.c ntfscluster.h cluster.c \ + cluster.h utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfscluster_OBJECTS = ntfscluster.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ cluster.$(OBJEXT) utils.$(OBJEXT) +ntfscluster_OBJECTS = $(am_ntfscluster_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfscluster_DEPENDENCIES = \ +@ENABLE_NTFSPROGS_TRUE@ $(am__DEPENDENCIES_2) +ntfscluster_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfscluster_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfscmp_SOURCES_DIST = ntfscmp.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfscmp_OBJECTS = ntfscmp.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfscmp_OBJECTS = $(am_ntfscmp_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfscmp_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfscmp_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfscmp_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfscp_SOURCES_DIST = ntfscp.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfscp_OBJECTS = ntfscp.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfscp_OBJECTS = $(am_ntfscp_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfscp_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfscp_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfscp_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsdecrypt_SOURCES_DIST = ntfsdecrypt.c utils.c utils.h +@ENABLE_CRYPTO_TRUE@@ENABLE_NTFSPROGS_TRUE@am_ntfsdecrypt_OBJECTS = ntfsdecrypt-ntfsdecrypt.$(OBJEXT) \ +@ENABLE_CRYPTO_TRUE@@ENABLE_NTFSPROGS_TRUE@ ntfsdecrypt-utils.$(OBJEXT) +ntfsdecrypt_OBJECTS = $(am_ntfsdecrypt_OBJECTS) +@ENABLE_CRYPTO_TRUE@@ENABLE_NTFSPROGS_TRUE@ntfsdecrypt_DEPENDENCIES = $(am__DEPENDENCIES_2) \ +@ENABLE_CRYPTO_TRUE@@ENABLE_NTFSPROGS_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_CRYPTO_TRUE@@ENABLE_NTFSPROGS_TRUE@ $(am__DEPENDENCIES_1) +ntfsdecrypt_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ntfsdecrypt_CFLAGS) \ + $(CFLAGS) $(ntfsdecrypt_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsdump_logfile_SOURCES_DIST = ntfsdump_logfile.c +@ENABLE_NTFSPROGS_TRUE@am_ntfsdump_logfile_OBJECTS = \ +@ENABLE_NTFSPROGS_TRUE@ ntfsdump_logfile.$(OBJEXT) +ntfsdump_logfile_OBJECTS = $(am_ntfsdump_logfile_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfsdump_logfile_DEPENDENCIES = \ +@ENABLE_NTFSPROGS_TRUE@ $(am__DEPENDENCIES_2) +ntfsdump_logfile_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(ntfsdump_logfile_LDFLAGS) $(LDFLAGS) \ + -o $@ +am__ntfsfallocate_SOURCES_DIST = ntfsfallocate.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfsfallocate_OBJECTS = \ +@ENABLE_NTFSPROGS_TRUE@ ntfsfallocate.$(OBJEXT) utils.$(OBJEXT) +ntfsfallocate_OBJECTS = $(am_ntfsfallocate_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfsfallocate_DEPENDENCIES = \ +@ENABLE_NTFSPROGS_TRUE@ $(am__DEPENDENCIES_2) +ntfsfallocate_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsfallocate_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsfix_SOURCES_DIST = ntfsfix.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfsfix_OBJECTS = ntfsfix.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfsfix_OBJECTS = $(am_ntfsfix_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfsfix_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsfix_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsfix_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsinfo_SOURCES_DIST = ntfsinfo.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfsinfo_OBJECTS = ntfsinfo.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfsinfo_OBJECTS = $(am_ntfsinfo_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfsinfo_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsinfo_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsinfo_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfslabel_SOURCES_DIST = ntfslabel.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfslabel_OBJECTS = ntfslabel.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfslabel_OBJECTS = $(am_ntfslabel_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfslabel_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfslabel_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfslabel_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsls_SOURCES_DIST = ntfsls.c utils.c utils.h list.h +@ENABLE_NTFSPROGS_TRUE@am_ntfsls_OBJECTS = ntfsls.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfsls_OBJECTS = $(am_ntfsls_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfsls_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsls_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsls_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsmftalloc_SOURCES_DIST = ntfsmftalloc.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfsmftalloc_OBJECTS = \ +@ENABLE_NTFSPROGS_TRUE@ ntfsmftalloc.$(OBJEXT) utils.$(OBJEXT) +ntfsmftalloc_OBJECTS = $(am_ntfsmftalloc_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfsmftalloc_DEPENDENCIES = \ +@ENABLE_NTFSPROGS_TRUE@ $(am__DEPENDENCIES_2) +ntfsmftalloc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsmftalloc_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsmove_SOURCES_DIST = ntfsmove.c ntfsmove.h utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfsmove_OBJECTS = ntfsmove.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfsmove_OBJECTS = $(am_ntfsmove_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfsmove_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfsmove_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsmove_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsresize_SOURCES_DIST = ntfsresize.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfsresize_OBJECTS = ntfsresize.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfsresize_OBJECTS = $(am_ntfsresize_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfsresize_DEPENDENCIES = \ +@ENABLE_NTFSPROGS_TRUE@ $(am__DEPENDENCIES_2) +ntfsresize_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsresize_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfstruncate_SOURCES_DIST = attrdef.c ntfstruncate.c utils.c \ + utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfstruncate_OBJECTS = attrdef.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ ntfstruncate.$(OBJEXT) utils.$(OBJEXT) +ntfstruncate_OBJECTS = $(am_ntfstruncate_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfstruncate_DEPENDENCIES = \ +@ENABLE_NTFSPROGS_TRUE@ $(am__DEPENDENCIES_2) +ntfstruncate_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfstruncate_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfsundelete_SOURCES_DIST = ntfsundelete.c ntfsundelete.h utils.c \ + utils.h list.h +@ENABLE_NTFSPROGS_TRUE@am_ntfsundelete_OBJECTS = \ +@ENABLE_NTFSPROGS_TRUE@ ntfsundelete.$(OBJEXT) utils.$(OBJEXT) +ntfsundelete_OBJECTS = $(am_ntfsundelete_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfsundelete_DEPENDENCIES = \ +@ENABLE_NTFSPROGS_TRUE@ $(am__DEPENDENCIES_2) +ntfsundelete_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfsundelete_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfswipe_SOURCES_DIST = ntfswipe.c ntfswipe.h utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@am_ntfswipe_OBJECTS = ntfswipe.$(OBJEXT) \ +@ENABLE_NTFSPROGS_TRUE@ utils.$(OBJEXT) +ntfswipe_OBJECTS = $(am_ntfswipe_OBJECTS) +@ENABLE_NTFSPROGS_TRUE@ntfswipe_DEPENDENCIES = $(am__DEPENDENCIES_2) +ntfswipe_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(ntfswipe_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(mkntfs_SOURCES) $(ntfscat_SOURCES) $(ntfsck_SOURCES) \ + $(ntfsclone_SOURCES) $(ntfscluster_SOURCES) $(ntfscmp_SOURCES) \ + $(ntfscp_SOURCES) $(ntfsdecrypt_SOURCES) \ + $(ntfsdump_logfile_SOURCES) $(ntfsfallocate_SOURCES) \ + $(ntfsfix_SOURCES) $(ntfsinfo_SOURCES) $(ntfslabel_SOURCES) \ + $(ntfsls_SOURCES) $(ntfsmftalloc_SOURCES) $(ntfsmove_SOURCES) \ + $(ntfsresize_SOURCES) $(ntfstruncate_SOURCES) \ + $(ntfsundelete_SOURCES) $(ntfswipe_SOURCES) +DIST_SOURCES = $(am__mkntfs_SOURCES_DIST) $(am__ntfscat_SOURCES_DIST) \ + $(am__ntfsck_SOURCES_DIST) $(am__ntfsclone_SOURCES_DIST) \ + $(am__ntfscluster_SOURCES_DIST) $(am__ntfscmp_SOURCES_DIST) \ + $(am__ntfscp_SOURCES_DIST) $(am__ntfsdecrypt_SOURCES_DIST) \ + $(am__ntfsdump_logfile_SOURCES_DIST) \ + $(am__ntfsfallocate_SOURCES_DIST) $(am__ntfsfix_SOURCES_DIST) \ + $(am__ntfsinfo_SOURCES_DIST) $(am__ntfslabel_SOURCES_DIST) \ + $(am__ntfsls_SOURCES_DIST) $(am__ntfsmftalloc_SOURCES_DIST) \ + $(am__ntfsmove_SOURCES_DIST) $(am__ntfsresize_SOURCES_DIST) \ + $(am__ntfstruncate_SOURCES_DIST) \ + $(am__ntfsundelete_SOURCES_DIST) $(am__ntfswipe_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(man_MANS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FUSE_MODULE_CFLAGS = @FUSE_MODULE_CFLAGS@ +FUSE_MODULE_LIBS = @FUSE_MODULE_LIBS@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDCONFIG = @LDCONFIG@ +LDFLAGS = @LDFLAGS@ +LIBFUSE_LITE_CFLAGS = @LIBFUSE_LITE_CFLAGS@ +LIBFUSE_LITE_LIBS = @LIBFUSE_LITE_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBNTFS_3G_VERSION = @LIBNTFS_3G_VERSION@ +LIBNTFS_CPPFLAGS = @LIBNTFS_CPPFLAGS@ +LIBNTFS_LIBS = @LIBNTFS_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MKNTFS_CPPFLAGS = @MKNTFS_CPPFLAGS@ +MKNTFS_LIBS = @MKNTFS_LIBS@ +MV = @MV@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NTFSPROGS_STATIC_LIBS = @NTFSPROGS_STATIC_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OUTPUT_FORMAT = @OUTPUT_FORMAT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +RM = @RM@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +all_includes = @all_includes@ +all_libraries = @all_libraries@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +ntfs3gincludedir = @ntfs3gincludedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rootbindir = @rootbindir@ +rootlibdir = @rootlibdir@ +rootsbindir = @rootsbindir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +@REALLYSTATIC_FALSE@AM_LIBS = $(top_builddir)/libntfs-3g/libntfs-3g.la +@REALLYSTATIC_TRUE@AM_LIBS = $(top_builddir)/libntfs-3g/.libs/libntfs-3g.a $(NTFSPROGS_STATIC_LIBS) +@REALLYSTATIC_FALSE@AM_LFLAGS = $(all_libraries) +# older builds may need -static instead of newer -all-static +@REALLYSTATIC_TRUE@AM_LFLAGS = -static +@REALLYSTATIC_TRUE@STATIC_LINK = $(CC) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +@REALLYSTATIC_FALSE@LIBTOOL_LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ + +# Workaround to make REALLYSTATIC work with automake 1.5. +LINK = $(STATIC_LINK) $(LIBTOOL_LINK) +@ENABLE_NTFSPROGS_TRUE@EXTRA_PROGRAM_NAMES = ntfswipe ntfstruncate \ +@ENABLE_NTFSPROGS_TRUE@ $(am__append_1) +@ENABLE_NTFSPROGS_TRUE@QUARANTINED_PROGRAM_NAMES = ntfsdump_logfile ntfsmftalloc ntfsmove ntfsck \ +@ENABLE_NTFSPROGS_TRUE@ ntfsfallocate + +@ENABLE_NTFSPROGS_TRUE@man_MANS = mkntfs.8 ntfsfix.8 ntfslabel.8 ntfsinfo.8 \ +@ENABLE_NTFSPROGS_TRUE@ ntfsundelete.8 ntfsresize.8 ntfsprogs.8 ntfsls.8 \ +@ENABLE_NTFSPROGS_TRUE@ ntfsclone.8 ntfscluster.8 ntfscat.8 ntfscp.8 \ +@ENABLE_NTFSPROGS_TRUE@ ntfscmp.8 ntfswipe.8 ntfstruncate.8 \ +@ENABLE_NTFSPROGS_TRUE@ ntfsdecrypt.8 ntfsfallocate.8 + +@ENABLE_NTFSPROGS_TRUE@EXTRA_MANS = +@ENABLE_NTFSPROGS_TRUE@CLEANFILES = $(EXTRA_PROGRAMS) +@ENABLE_NTFSPROGS_TRUE@MAINTAINERCLEANFILES = Makefile.in + +# Set the include path. +@ENABLE_NTFSPROGS_TRUE@AM_CPPFLAGS = -I$(top_srcdir)/include/ntfs-3g $(all_includes) +@ENABLE_NTFSPROGS_TRUE@ntfsfix_SOURCES = ntfsfix.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfsfix_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfsfix_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@mkntfs_CPPFLAGS = $(AM_CPPFLAGS) $(MKNTFS_CPPFLAGS) +@ENABLE_NTFSPROGS_TRUE@mkntfs_SOURCES = attrdef.c attrdef.h boot.c boot.h sd.c sd.h mkntfs.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@mkntfs_LDADD = $(AM_LIBS) $(MKNTFS_LIBS) +@ENABLE_NTFSPROGS_TRUE@mkntfs_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfslabel_SOURCES = ntfslabel.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfslabel_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfslabel_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfsinfo_SOURCES = ntfsinfo.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfsinfo_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfsinfo_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfsundelete_SOURCES = ntfsundelete.c ntfsundelete.h utils.c utils.h list.h +@ENABLE_NTFSPROGS_TRUE@ntfsundelete_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfsundelete_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfsresize_SOURCES = ntfsresize.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfsresize_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfsresize_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfsclone_SOURCES = ntfsclone.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfsclone_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfsclone_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfscluster_SOURCES = ntfscluster.c ntfscluster.h cluster.c cluster.h utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfscluster_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfscluster_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfsls_SOURCES = ntfsls.c utils.c utils.h list.h +@ENABLE_NTFSPROGS_TRUE@ntfsls_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfsls_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfscat_SOURCES = ntfscat.c ntfscat.h utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfscat_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfscat_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfscp_SOURCES = ntfscp.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfscp_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfscp_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfsck_SOURCES = ntfsck.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfsck_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfsck_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfscmp_SOURCES = ntfscmp.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfscmp_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfscmp_LDFLAGS = $(AM_LFLAGS) + +# We don't distribute these +@ENABLE_NTFSPROGS_TRUE@ntfstruncate_SOURCES = attrdef.c ntfstruncate.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfstruncate_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfstruncate_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfsmftalloc_SOURCES = ntfsmftalloc.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfsmftalloc_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfsmftalloc_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfsmove_SOURCES = ntfsmove.c ntfsmove.h utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfsmove_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfsmove_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfswipe_SOURCES = ntfswipe.c ntfswipe.h utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfswipe_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfswipe_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfsdump_logfile_SOURCES = ntfsdump_logfile.c +@ENABLE_NTFSPROGS_TRUE@ntfsdump_logfile_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfsdump_logfile_LDFLAGS = $(AM_LFLAGS) +@ENABLE_NTFSPROGS_TRUE@ntfsfallocate_SOURCES = ntfsfallocate.c utils.c utils.h +@ENABLE_NTFSPROGS_TRUE@ntfsfallocate_LDADD = $(AM_LIBS) +@ENABLE_NTFSPROGS_TRUE@ntfsfallocate_LDFLAGS = $(AM_LFLAGS) +@ENABLE_CRYPTO_TRUE@@ENABLE_NTFSPROGS_TRUE@ntfsdecrypt_SOURCES = ntfsdecrypt.c utils.c utils.h +@ENABLE_CRYPTO_TRUE@@ENABLE_NTFSPROGS_TRUE@ntfsdecrypt_LDADD = $(AM_LIBS) $(GNUTLS_LIBS) $(LIBGCRYPT_LIBS) +@ENABLE_CRYPTO_TRUE@@ENABLE_NTFSPROGS_TRUE@ntfsdecrypt_LDFLAGS = $(AM_LFLAGS) +@ENABLE_CRYPTO_TRUE@@ENABLE_NTFSPROGS_TRUE@ntfsdecrypt_CFLAGS = $(GNUTLS_CFLAGS) $(LIBGCRYPT_CFLAGS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu ntfsprogs/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu ntfsprogs/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +mkntfs.8: $(top_builddir)/config.status $(srcdir)/mkntfs.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfscat.8: $(top_builddir)/config.status $(srcdir)/ntfscat.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsclone.8: $(top_builddir)/config.status $(srcdir)/ntfsclone.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfscluster.8: $(top_builddir)/config.status $(srcdir)/ntfscluster.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfscmp.8: $(top_builddir)/config.status $(srcdir)/ntfscmp.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfscp.8: $(top_builddir)/config.status $(srcdir)/ntfscp.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsfix.8: $(top_builddir)/config.status $(srcdir)/ntfsfix.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsinfo.8: $(top_builddir)/config.status $(srcdir)/ntfsinfo.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfslabel.8: $(top_builddir)/config.status $(srcdir)/ntfslabel.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsls.8: $(top_builddir)/config.status $(srcdir)/ntfsls.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsprogs.8: $(top_builddir)/config.status $(srcdir)/ntfsprogs.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsresize.8: $(top_builddir)/config.status $(srcdir)/ntfsresize.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsundelete.8: $(top_builddir)/config.status $(srcdir)/ntfsundelete.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsdecrypt.8: $(top_builddir)/config.status $(srcdir)/ntfsdecrypt.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfswipe.8: $(top_builddir)/config.status $(srcdir)/ntfswipe.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfstruncate.8: $(top_builddir)/config.status $(srcdir)/ntfstruncate.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfsfallocate.8: $(top_builddir)/config.status $(srcdir)/ntfsfallocate.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +mkntfs$(EXEEXT): $(mkntfs_OBJECTS) $(mkntfs_DEPENDENCIES) $(EXTRA_mkntfs_DEPENDENCIES) + @rm -f mkntfs$(EXEEXT) + $(AM_V_CCLD)$(mkntfs_LINK) $(mkntfs_OBJECTS) $(mkntfs_LDADD) $(LIBS) + +ntfscat$(EXEEXT): $(ntfscat_OBJECTS) $(ntfscat_DEPENDENCIES) $(EXTRA_ntfscat_DEPENDENCIES) + @rm -f ntfscat$(EXEEXT) + $(AM_V_CCLD)$(ntfscat_LINK) $(ntfscat_OBJECTS) $(ntfscat_LDADD) $(LIBS) + +ntfsck$(EXEEXT): $(ntfsck_OBJECTS) $(ntfsck_DEPENDENCIES) $(EXTRA_ntfsck_DEPENDENCIES) + @rm -f ntfsck$(EXEEXT) + $(AM_V_CCLD)$(ntfsck_LINK) $(ntfsck_OBJECTS) $(ntfsck_LDADD) $(LIBS) + +ntfsclone$(EXEEXT): $(ntfsclone_OBJECTS) $(ntfsclone_DEPENDENCIES) $(EXTRA_ntfsclone_DEPENDENCIES) + @rm -f ntfsclone$(EXEEXT) + $(AM_V_CCLD)$(ntfsclone_LINK) $(ntfsclone_OBJECTS) $(ntfsclone_LDADD) $(LIBS) + +ntfscluster$(EXEEXT): $(ntfscluster_OBJECTS) $(ntfscluster_DEPENDENCIES) $(EXTRA_ntfscluster_DEPENDENCIES) + @rm -f ntfscluster$(EXEEXT) + $(AM_V_CCLD)$(ntfscluster_LINK) $(ntfscluster_OBJECTS) $(ntfscluster_LDADD) $(LIBS) + +ntfscmp$(EXEEXT): $(ntfscmp_OBJECTS) $(ntfscmp_DEPENDENCIES) $(EXTRA_ntfscmp_DEPENDENCIES) + @rm -f ntfscmp$(EXEEXT) + $(AM_V_CCLD)$(ntfscmp_LINK) $(ntfscmp_OBJECTS) $(ntfscmp_LDADD) $(LIBS) + +ntfscp$(EXEEXT): $(ntfscp_OBJECTS) $(ntfscp_DEPENDENCIES) $(EXTRA_ntfscp_DEPENDENCIES) + @rm -f ntfscp$(EXEEXT) + $(AM_V_CCLD)$(ntfscp_LINK) $(ntfscp_OBJECTS) $(ntfscp_LDADD) $(LIBS) + +ntfsdecrypt$(EXEEXT): $(ntfsdecrypt_OBJECTS) $(ntfsdecrypt_DEPENDENCIES) $(EXTRA_ntfsdecrypt_DEPENDENCIES) + @rm -f ntfsdecrypt$(EXEEXT) + $(AM_V_CCLD)$(ntfsdecrypt_LINK) $(ntfsdecrypt_OBJECTS) $(ntfsdecrypt_LDADD) $(LIBS) + +ntfsdump_logfile$(EXEEXT): $(ntfsdump_logfile_OBJECTS) $(ntfsdump_logfile_DEPENDENCIES) $(EXTRA_ntfsdump_logfile_DEPENDENCIES) + @rm -f ntfsdump_logfile$(EXEEXT) + $(AM_V_CCLD)$(ntfsdump_logfile_LINK) $(ntfsdump_logfile_OBJECTS) $(ntfsdump_logfile_LDADD) $(LIBS) + +ntfsfallocate$(EXEEXT): $(ntfsfallocate_OBJECTS) $(ntfsfallocate_DEPENDENCIES) $(EXTRA_ntfsfallocate_DEPENDENCIES) + @rm -f ntfsfallocate$(EXEEXT) + $(AM_V_CCLD)$(ntfsfallocate_LINK) $(ntfsfallocate_OBJECTS) $(ntfsfallocate_LDADD) $(LIBS) + +ntfsfix$(EXEEXT): $(ntfsfix_OBJECTS) $(ntfsfix_DEPENDENCIES) $(EXTRA_ntfsfix_DEPENDENCIES) + @rm -f ntfsfix$(EXEEXT) + $(AM_V_CCLD)$(ntfsfix_LINK) $(ntfsfix_OBJECTS) $(ntfsfix_LDADD) $(LIBS) + +ntfsinfo$(EXEEXT): $(ntfsinfo_OBJECTS) $(ntfsinfo_DEPENDENCIES) $(EXTRA_ntfsinfo_DEPENDENCIES) + @rm -f ntfsinfo$(EXEEXT) + $(AM_V_CCLD)$(ntfsinfo_LINK) $(ntfsinfo_OBJECTS) $(ntfsinfo_LDADD) $(LIBS) + +ntfslabel$(EXEEXT): $(ntfslabel_OBJECTS) $(ntfslabel_DEPENDENCIES) $(EXTRA_ntfslabel_DEPENDENCIES) + @rm -f ntfslabel$(EXEEXT) + $(AM_V_CCLD)$(ntfslabel_LINK) $(ntfslabel_OBJECTS) $(ntfslabel_LDADD) $(LIBS) + +ntfsls$(EXEEXT): $(ntfsls_OBJECTS) $(ntfsls_DEPENDENCIES) $(EXTRA_ntfsls_DEPENDENCIES) + @rm -f ntfsls$(EXEEXT) + $(AM_V_CCLD)$(ntfsls_LINK) $(ntfsls_OBJECTS) $(ntfsls_LDADD) $(LIBS) + +ntfsmftalloc$(EXEEXT): $(ntfsmftalloc_OBJECTS) $(ntfsmftalloc_DEPENDENCIES) $(EXTRA_ntfsmftalloc_DEPENDENCIES) + @rm -f ntfsmftalloc$(EXEEXT) + $(AM_V_CCLD)$(ntfsmftalloc_LINK) $(ntfsmftalloc_OBJECTS) $(ntfsmftalloc_LDADD) $(LIBS) + +ntfsmove$(EXEEXT): $(ntfsmove_OBJECTS) $(ntfsmove_DEPENDENCIES) $(EXTRA_ntfsmove_DEPENDENCIES) + @rm -f ntfsmove$(EXEEXT) + $(AM_V_CCLD)$(ntfsmove_LINK) $(ntfsmove_OBJECTS) $(ntfsmove_LDADD) $(LIBS) + +ntfsresize$(EXEEXT): $(ntfsresize_OBJECTS) $(ntfsresize_DEPENDENCIES) $(EXTRA_ntfsresize_DEPENDENCIES) + @rm -f ntfsresize$(EXEEXT) + $(AM_V_CCLD)$(ntfsresize_LINK) $(ntfsresize_OBJECTS) $(ntfsresize_LDADD) $(LIBS) + +ntfstruncate$(EXEEXT): $(ntfstruncate_OBJECTS) $(ntfstruncate_DEPENDENCIES) $(EXTRA_ntfstruncate_DEPENDENCIES) + @rm -f ntfstruncate$(EXEEXT) + $(AM_V_CCLD)$(ntfstruncate_LINK) $(ntfstruncate_OBJECTS) $(ntfstruncate_LDADD) $(LIBS) + +ntfsundelete$(EXEEXT): $(ntfsundelete_OBJECTS) $(ntfsundelete_DEPENDENCIES) $(EXTRA_ntfsundelete_DEPENDENCIES) + @rm -f ntfsundelete$(EXEEXT) + $(AM_V_CCLD)$(ntfsundelete_LINK) $(ntfsundelete_OBJECTS) $(ntfsundelete_LDADD) $(LIBS) + +ntfswipe$(EXEEXT): $(ntfswipe_OBJECTS) $(ntfswipe_DEPENDENCIES) $(EXTRA_ntfswipe_DEPENDENCIES) + @rm -f ntfswipe$(EXEEXT) + $(AM_V_CCLD)$(ntfswipe_LINK) $(ntfswipe_OBJECTS) $(ntfswipe_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/attrdef.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cluster.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkntfs-attrdef.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkntfs-boot.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkntfs-mkntfs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkntfs-sd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkntfs-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfscat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsck.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsclone.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfscluster.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfscmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfscp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsdecrypt-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsdump_logfile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsfallocate.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsfix.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsinfo.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfslabel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsls.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsmftalloc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsmove.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsresize.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfstruncate.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfsundelete.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfswipe.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mkntfs-attrdef.o: attrdef.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-attrdef.o -MD -MP -MF $(DEPDIR)/mkntfs-attrdef.Tpo -c -o mkntfs-attrdef.o `test -f 'attrdef.c' || echo '$(srcdir)/'`attrdef.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-attrdef.Tpo $(DEPDIR)/mkntfs-attrdef.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attrdef.c' object='mkntfs-attrdef.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-attrdef.o `test -f 'attrdef.c' || echo '$(srcdir)/'`attrdef.c + +mkntfs-attrdef.obj: attrdef.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-attrdef.obj -MD -MP -MF $(DEPDIR)/mkntfs-attrdef.Tpo -c -o mkntfs-attrdef.obj `if test -f 'attrdef.c'; then $(CYGPATH_W) 'attrdef.c'; else $(CYGPATH_W) '$(srcdir)/attrdef.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-attrdef.Tpo $(DEPDIR)/mkntfs-attrdef.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='attrdef.c' object='mkntfs-attrdef.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-attrdef.obj `if test -f 'attrdef.c'; then $(CYGPATH_W) 'attrdef.c'; else $(CYGPATH_W) '$(srcdir)/attrdef.c'; fi` + +mkntfs-boot.o: boot.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-boot.o -MD -MP -MF $(DEPDIR)/mkntfs-boot.Tpo -c -o mkntfs-boot.o `test -f 'boot.c' || echo '$(srcdir)/'`boot.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-boot.Tpo $(DEPDIR)/mkntfs-boot.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='boot.c' object='mkntfs-boot.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-boot.o `test -f 'boot.c' || echo '$(srcdir)/'`boot.c + +mkntfs-boot.obj: boot.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-boot.obj -MD -MP -MF $(DEPDIR)/mkntfs-boot.Tpo -c -o mkntfs-boot.obj `if test -f 'boot.c'; then $(CYGPATH_W) 'boot.c'; else $(CYGPATH_W) '$(srcdir)/boot.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-boot.Tpo $(DEPDIR)/mkntfs-boot.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='boot.c' object='mkntfs-boot.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-boot.obj `if test -f 'boot.c'; then $(CYGPATH_W) 'boot.c'; else $(CYGPATH_W) '$(srcdir)/boot.c'; fi` + +mkntfs-sd.o: sd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-sd.o -MD -MP -MF $(DEPDIR)/mkntfs-sd.Tpo -c -o mkntfs-sd.o `test -f 'sd.c' || echo '$(srcdir)/'`sd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-sd.Tpo $(DEPDIR)/mkntfs-sd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sd.c' object='mkntfs-sd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-sd.o `test -f 'sd.c' || echo '$(srcdir)/'`sd.c + +mkntfs-sd.obj: sd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-sd.obj -MD -MP -MF $(DEPDIR)/mkntfs-sd.Tpo -c -o mkntfs-sd.obj `if test -f 'sd.c'; then $(CYGPATH_W) 'sd.c'; else $(CYGPATH_W) '$(srcdir)/sd.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-sd.Tpo $(DEPDIR)/mkntfs-sd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sd.c' object='mkntfs-sd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-sd.obj `if test -f 'sd.c'; then $(CYGPATH_W) 'sd.c'; else $(CYGPATH_W) '$(srcdir)/sd.c'; fi` + +mkntfs-mkntfs.o: mkntfs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-mkntfs.o -MD -MP -MF $(DEPDIR)/mkntfs-mkntfs.Tpo -c -o mkntfs-mkntfs.o `test -f 'mkntfs.c' || echo '$(srcdir)/'`mkntfs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-mkntfs.Tpo $(DEPDIR)/mkntfs-mkntfs.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mkntfs.c' object='mkntfs-mkntfs.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-mkntfs.o `test -f 'mkntfs.c' || echo '$(srcdir)/'`mkntfs.c + +mkntfs-mkntfs.obj: mkntfs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-mkntfs.obj -MD -MP -MF $(DEPDIR)/mkntfs-mkntfs.Tpo -c -o mkntfs-mkntfs.obj `if test -f 'mkntfs.c'; then $(CYGPATH_W) 'mkntfs.c'; else $(CYGPATH_W) '$(srcdir)/mkntfs.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-mkntfs.Tpo $(DEPDIR)/mkntfs-mkntfs.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mkntfs.c' object='mkntfs-mkntfs.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-mkntfs.obj `if test -f 'mkntfs.c'; then $(CYGPATH_W) 'mkntfs.c'; else $(CYGPATH_W) '$(srcdir)/mkntfs.c'; fi` + +mkntfs-utils.o: utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-utils.o -MD -MP -MF $(DEPDIR)/mkntfs-utils.Tpo -c -o mkntfs-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-utils.Tpo $(DEPDIR)/mkntfs-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='mkntfs-utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c + +mkntfs-utils.obj: utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mkntfs-utils.obj -MD -MP -MF $(DEPDIR)/mkntfs-utils.Tpo -c -o mkntfs-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mkntfs-utils.Tpo $(DEPDIR)/mkntfs-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='mkntfs-utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mkntfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mkntfs-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` + +ntfsdecrypt-ntfsdecrypt.o: ntfsdecrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -MT ntfsdecrypt-ntfsdecrypt.o -MD -MP -MF $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Tpo -c -o ntfsdecrypt-ntfsdecrypt.o `test -f 'ntfsdecrypt.c' || echo '$(srcdir)/'`ntfsdecrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Tpo $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ntfsdecrypt.c' object='ntfsdecrypt-ntfsdecrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -c -o ntfsdecrypt-ntfsdecrypt.o `test -f 'ntfsdecrypt.c' || echo '$(srcdir)/'`ntfsdecrypt.c + +ntfsdecrypt-ntfsdecrypt.obj: ntfsdecrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -MT ntfsdecrypt-ntfsdecrypt.obj -MD -MP -MF $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Tpo -c -o ntfsdecrypt-ntfsdecrypt.obj `if test -f 'ntfsdecrypt.c'; then $(CYGPATH_W) 'ntfsdecrypt.c'; else $(CYGPATH_W) '$(srcdir)/ntfsdecrypt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Tpo $(DEPDIR)/ntfsdecrypt-ntfsdecrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ntfsdecrypt.c' object='ntfsdecrypt-ntfsdecrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -c -o ntfsdecrypt-ntfsdecrypt.obj `if test -f 'ntfsdecrypt.c'; then $(CYGPATH_W) 'ntfsdecrypt.c'; else $(CYGPATH_W) '$(srcdir)/ntfsdecrypt.c'; fi` + +ntfsdecrypt-utils.o: utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -MT ntfsdecrypt-utils.o -MD -MP -MF $(DEPDIR)/ntfsdecrypt-utils.Tpo -c -o ntfsdecrypt-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfsdecrypt-utils.Tpo $(DEPDIR)/ntfsdecrypt-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='ntfsdecrypt-utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -c -o ntfsdecrypt-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c + +ntfsdecrypt-utils.obj: utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -MT ntfsdecrypt-utils.obj -MD -MP -MF $(DEPDIR)/ntfsdecrypt-utils.Tpo -c -o ntfsdecrypt-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfsdecrypt-utils.Tpo $(DEPDIR)/ntfsdecrypt-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='ntfsdecrypt-utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfsdecrypt_CFLAGS) $(CFLAGS) -c -o ntfsdecrypt-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man8: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man8dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.8[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ + done; } + +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man8dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +@ENABLE_NTFSPROGS_FALSE@uninstall-local: +@ENABLE_NTFSPROGS_FALSE@install-exec-hook: +@ENABLE_NTFSPROGS_FALSE@install-data-hook: +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool \ + clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-man + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-sbinPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man8 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-local uninstall-man \ + uninstall-sbinPROGRAMS + +uninstall-man: uninstall-man8 + +.MAKE: install-am install-data-am install-exec-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool \ + clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-data-hook install-dvi install-dvi-am \ + install-exec install-exec-am install-exec-hook install-html \ + install-html-am install-info install-info-am install-man \ + install-man8 install-pdf install-pdf-am install-ps \ + install-ps-am install-sbinPROGRAMS install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ + uninstall-local uninstall-man uninstall-man8 \ + uninstall-sbinPROGRAMS + + +# Extra targets + +@ENABLE_NTFSPROGS_TRUE@strip: $(bin_PROGRAMS) $(sbin_PROGRAMS) +@ENABLE_NTFSPROGS_TRUE@ $(STRIP) $^ + +@ENABLE_NTFSPROGS_TRUE@libs: +@ENABLE_NTFSPROGS_TRUE@ (cd ../libntfs-3g && $(MAKE) libs) || exit 1; + +@ENABLE_NTFSPROGS_TRUE@extra: extras + +@ENABLE_NTFSPROGS_TRUE@extras: libs $(EXTRA_PROGRAMS) + +# mkfs.ntfs[.8] hard link + +@ENABLE_NTFSPROGS_TRUE@install-exec-hook: +@ENABLE_NTFSPROGS_TRUE@ $(INSTALL) -d $(DESTDIR)/sbin +@ENABLE_NTFSPROGS_TRUE@ $(LN_S) -f $(sbindir)/mkntfs $(DESTDIR)/sbin/mkfs.ntfs + +@ENABLE_NTFSPROGS_TRUE@install-data-hook: +@ENABLE_NTFSPROGS_TRUE@ $(INSTALL) -d $(DESTDIR)$(man8dir) +@ENABLE_NTFSPROGS_TRUE@ $(LN_S) -f mkntfs.8 $(DESTDIR)$(man8dir)/mkfs.ntfs.8 + +@ENABLE_NTFSPROGS_TRUE@uninstall-local: +@ENABLE_NTFSPROGS_TRUE@ $(RM) -f $(DESTDIR)/sbin/mkfs.ntfs +@ENABLE_NTFSPROGS_TRUE@ $(RM) -f $(DESTDIR)$(man8dir)/mkfs.ntfs.8 + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ntfsprogs/attrdef.c b/ntfsprogs/attrdef.c new file mode 100755 index 0000000000000000000000000000000000000000..36501e5c276c474e7bd700f28ba22bcc2cadfd88 --- /dev/null +++ b/ntfsprogs/attrdef.c @@ -0,0 +1,168 @@ +#include "attrdef.h" + +/** + * attrdef_ntfs3x_array + */ +const unsigned char attrdef_ntfs3x_array[2560] = { +0x24, 0x00, 0x53, 0x00, 0x54, 0x00, 0x41, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x41, 0x00, 0x52, 0x00, +0x44, 0x00, 0x5F, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, 0x52, 0x00, 0x4D, 0x00, +0x41, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x41, 0x00, 0x54, 0x00, 0x54, 0x00, 0x52, 0x00, 0x49, 0x00, 0x42, 0x00, 0x55, 0x00, +0x54, 0x00, 0x45, 0x00, 0x5F, 0x00, 0x4C, 0x00, 0x49, 0x00, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4C, 0x00, 0x45, 0x00, 0x5F, 0x00, 0x4E, 0x00, 0x41, 0x00, +0x4D, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, +0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x4F, 0x00, 0x42, 0x00, 0x4A, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x5F, 0x00, +0x49, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x53, 0x00, 0x45, 0x00, 0x43, 0x00, 0x55, 0x00, 0x52, 0x00, 0x49, 0x00, 0x54, 0x00, +0x59, 0x00, 0x5F, 0x00, 0x44, 0x00, 0x45, 0x00, 0x53, 0x00, 0x43, 0x00, 0x52, 0x00, 0x49, 0x00, +0x50, 0x00, 0x54, 0x00, 0x4F, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x56, 0x00, 0x4F, 0x00, 0x4C, 0x00, 0x55, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x5F, 0x00, +0x4E, 0x00, 0x41, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x56, 0x00, 0x4F, 0x00, 0x4C, 0x00, 0x55, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x5F, 0x00, +0x49, 0x00, 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, 0x52, 0x00, 0x4D, 0x00, 0x41, 0x00, 0x54, 0x00, +0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x45, 0x00, 0x58, 0x00, 0x5F, 0x00, 0x52, 0x00, +0x4F, 0x00, 0x4F, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x45, 0x00, 0x58, 0x00, 0x5F, 0x00, 0x41, 0x00, +0x4C, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x43, 0x00, 0x41, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4F, 0x00, +0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x42, 0x00, 0x49, 0x00, 0x54, 0x00, 0x4D, 0x00, 0x41, 0x00, 0x50, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x24, 0x00, 0x52, 0x00, 0x45, 0x00, 0x50, 0x00, 0x41, 0x00, 0x52, 0x00, 0x53, 0x00, 0x45, 0x00, +0x5F, 0x00, 0x50, 0x00, 0x4F, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x45, 0x00, 0x41, 0x00, 0x5F, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, +0x52, 0x00, 0x4D, 0x00, 0x41, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x45, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +0x24, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x47, 0x00, 0x47, 0x00, 0x45, 0x00, 0x44, 0x00, 0x5F, 0x00, +0x55, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4C, 0x00, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5F, 0x00, +0x53, 0x00, 0x54, 0x00, 0x52, 0x00, 0x45, 0x00, 0x41, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + diff --git a/ntfsprogs/attrdef.h b/ntfsprogs/attrdef.h new file mode 100755 index 0000000000000000000000000000000000000000..0c664394e0ccf7d09938f92010756d9c83499918 --- /dev/null +++ b/ntfsprogs/attrdef.h @@ -0,0 +1,7 @@ +#ifndef _NTFS_ATTRDEF_H_ +#define _NTFS_ATTRDEF_H_ + +extern const unsigned char attrdef_ntfs3x_array[2560]; + +#endif /* _NTFS_ATTRDEF_H_ */ + diff --git a/ntfsprogs/boot.c b/ntfsprogs/boot.c new file mode 100755 index 0000000000000000000000000000000000000000..9272be9fb1d2fb0e69f763f3b2f78e85b503fac6 --- /dev/null +++ b/ntfsprogs/boot.c @@ -0,0 +1,268 @@ +#include "boot.h" + +/** + * boot_array - the first 4136 bytes of $Boot + * + * The first 4136 bytes of $Boot. The rest is just zero. Total 8192 bytes. + */ +const unsigned char boot_array[4136] = { +235, 82, 144, 78, 84, 70, 83, 32, 32, 32, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 250, 51, 192, 142, 208, 188, 0, 124, 251, 104, 192, 7, + 31, 30, 104, 102, 0, 203, 136, 22, 14, 0, 102, 129, 62, 3, 0, 78, + 84, 70, 83, 117, 21, 180, 65, 187, 170, 85, 205, 19, 114, 12, 129, 251, + 85, 170, 117, 6, 247, 193, 1, 0, 117, 3, 233, 210, 0, 30, 131, 236, + 24, 104, 26, 0, 180, 72, 138, 22, 14, 0, 139, 244, 22, 31, 205, 19, +159, 131, 196, 24, 158, 88, 31, 114, 225, 59, 6, 11, 0, 117, 219, 163, + 15, 0, 193, 46, 15, 0, 4, 30, 90, 51, 219, 185, 0, 32, 43, 200, +102, 255, 6, 17, 0, 3, 22, 15, 0, 142, 194, 255, 6, 22, 0, 232, + 64, 0, 43, 200, 119, 239, 184, 0, 187, 205, 26, 102, 35, 192, 117, 45, +102, 129, 251, 84, 67, 80, 65, 117, 36, 129, 249, 2, 1, 114, 30, 22, +104, 7, 187, 22, 104, 112, 14, 22, 104, 9, 0, 102, 83, 102, 83, 102, + 85, 22, 22, 22, 104, 184, 1, 102, 97, 14, 7, 205, 26, 233, 106, 1, +144, 144, 102, 96, 30, 6, 102, 161, 17, 0, 102, 3, 6, 28, 0, 30, +102, 104, 0, 0, 0, 0, 102, 80, 6, 83, 104, 1, 0, 104, 16, 0, +180, 66, 138, 22, 14, 0, 22, 31, 139, 244, 205, 19, 102, 89, 91, 90, +102, 89, 102, 89, 31, 15, 130, 22, 0, 102, 255, 6, 17, 0, 3, 22, + 15, 0, 142, 194, 255, 14, 22, 0, 117, 188, 7, 31, 102, 97, 195, 160, +248, 1, 232, 8, 0, 160, 251, 1, 232, 2, 0, 235, 254, 180, 1, 139, +240, 172, 60, 0, 116, 9, 180, 14, 187, 7, 0, 205, 16, 235, 242, 195, + 13, 10, 65, 32, 100, 105, 115, 107, 32, 114, 101, 97, 100, 32, 101, 114, +114, 111, 114, 32, 111, 99, 99, 117, 114, 114, 101, 100, 0, 13, 10, 66, + 79, 79, 84, 77, 71, 82, 32, 105, 115, 32, 109, 105, 115, 115, 105, 110, +103, 0, 13, 10, 66, 79, 79, 84, 77, 71, 82, 32, 105, 115, 32, 99, +111, 109, 112, 114, 101, 115, 115, 101, 100, 0, 13, 10, 80, 114, 101, 115, +115, 32, 67, 116, 114, 108, 43, 65, 108, 116, 43, 68, 101, 108, 32, 116, +111, 32, 114, 101, 115, 116, 97, 114, 116, 13, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 128, 157, 178, 202, 0, 0, 85, 170, + 7, 0, 66, 0, 79, 0, 79, 0, 84, 0, 77, 0, 71, 0, 82, 0, + 4, 0, 36, 0, 73, 0, 51, 0, 48, 0, 0, 224, 0, 0, 0, 48, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 235, 34, 144, 144, 5, 0, 78, 0, 84, 0, + 76, 0, 68, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 15, 183, 6, 11, 0, +102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 163, 82, 2, 102, 139, 14, + 64, 0, 128, 249, 0, 15, 143, 14, 0, 246, 217, 102, 184, 1, 0, 0, + 0, 102, 211, 224, 235, 8, 144, 102, 161, 82, 2, 102, 247, 225, 102, 163, +102, 2, 102, 15, 183, 30, 11, 0, 102, 51, 210, 102, 247, 243, 102, 163, + 86, 2, 232, 149, 4, 102, 139, 14, 78, 2, 102, 137, 14, 38, 2, 102, + 3, 14, 102, 2, 102, 137, 14, 42, 2, 102, 3, 14, 102, 2, 102, 137, + 14, 46, 2, 102, 3, 14, 102, 2, 102, 137, 14, 62, 2, 102, 3, 14, +102, 2, 102, 137, 14, 70, 2, 102, 184, 144, 0, 0, 0, 102, 139, 14, + 38, 2, 232, 131, 9, 102, 11, 192, 15, 132, 83, 254, 102, 163, 50, 2, +102, 184, 160, 0, 0, 0, 102, 139, 14, 42, 2, 232, 106, 9, 102, 163, + 54, 2, 102, 184, 176, 0, 0, 0, 102, 139, 14, 46, 2, 232, 88, 9, +102, 163, 58, 2, 102, 161, 50, 2, 102, 11, 192, 15, 132, 32, 254, 103, +128, 120, 8, 0, 15, 133, 23, 254, 103, 102, 141, 80, 16, 103, 3, 66, + 4, 103, 102, 15, 182, 72, 12, 102, 137, 14, 114, 2, 103, 102, 139, 72, + 8, 102, 137, 14, 110, 2, 102, 161, 110, 2, 102, 15, 183, 14, 11, 0, +102, 51, 210, 102, 247, 241, 102, 163, 118, 2, 102, 161, 70, 2, 102, 3, + 6, 110, 2, 102, 163, 74, 2, 102, 131, 62, 54, 2, 0, 15, 132, 29, + 0, 102, 131, 62, 58, 2, 0, 15, 132, 196, 253, 102, 139, 30, 58, 2, + 30, 7, 102, 139, 62, 74, 2, 102, 161, 46, 2, 232, 224, 1, 102, 15, +183, 14, 0, 2, 102, 184, 2, 2, 0, 0, 232, 34, 8, 102, 11, 192, + 15, 133, 22, 0, 102, 15, 183, 14, 90, 2, 102, 184, 92, 2, 0, 0, +232, 12, 8, 102, 11, 192, 15, 132, 66, 12, 103, 102, 139, 0, 30, 7, +102, 139, 62, 62, 2, 232, 63, 6, 102, 161, 62, 2, 102, 187, 32, 0, + 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 228, + 0, 102, 133, 192, 15, 133, 35, 0, 102, 161, 62, 2, 102, 187, 128, 0, + 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 196, + 0, 102, 11, 192, 15, 133, 68, 0, 233, 241, 11, 102, 51, 210, 102, 185, +128, 0, 0, 0, 102, 161, 62, 2, 232, 202, 8, 102, 11, 192, 15, 132, +218, 11, 30, 7, 102, 139, 62, 62, 2, 232, 219, 5, 102, 161, 62, 2, +102, 187, 128, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, + 0, 0, 232, 128, 0, 102, 11, 192, 15, 132, 176, 11, 103, 102, 15, 183, + 88, 12, 102, 129, 227, 255, 0, 0, 0, 15, 133, 165, 11, 102, 139, 216, +104, 0, 32, 7, 102, 43, 255, 102, 161, 62, 2, 232, 0, 1, 104, 0, + 32, 7, 102, 43, 255, 102, 161, 62, 2, 232, 172, 10, 138, 22, 14, 0, +184, 232, 3, 142, 192, 141, 54, 11, 0, 43, 192, 104, 0, 32, 80, 203, + 6, 30, 102, 96, 102, 139, 218, 102, 15, 182, 14, 13, 0, 102, 247, 225, +102, 163, 17, 0, 102, 139, 195, 102, 247, 225, 163, 22, 0, 139, 223, 131, +227, 15, 140, 192, 102, 193, 239, 4, 3, 199, 80, 7, 232, 51, 252, 102, + 97, 144, 31, 7, 195, 103, 3, 64, 20, 103, 102, 131, 56, 255, 15, 132, + 76, 0, 103, 102, 57, 24, 15, 133, 51, 0, 102, 11, 201, 15, 133, 10, + 0, 103, 128, 120, 9, 0, 15, 133, 35, 0, 195, 103, 58, 72, 9, 15, +133, 26, 0, 102, 139, 240, 103, 3, 112, 10, 232, 151, 6, 102, 81, 30, + 7, 102, 139, 250, 243, 167, 102, 89, 15, 133, 1, 0, 195, 103, 102, 131, +120, 4, 0, 15, 132, 7, 0, 103, 102, 3, 64, 4, 235, 171, 102, 43, +192, 195, 102, 139, 243, 232, 108, 6, 103, 102, 3, 0, 103, 247, 64, 12, + 2, 0, 15, 133, 52, 0, 103, 102, 141, 80, 16, 103, 58, 74, 64, 15, +133, 24, 0, 103, 102, 141, 114, 66, 232, 73, 6, 102, 81, 30, 7, 102, +139, 251, 243, 167, 102, 89, 15, 133, 1, 0, 195, 103, 131, 120, 8, 0, + 15, 132, 6, 0, 103, 3, 64, 8, 235, 194, 102, 51, 192, 195, 103, 128, +123, 8, 0, 15, 133, 28, 0, 6, 30, 102, 96, 103, 102, 141, 83, 16, +103, 102, 139, 10, 102, 139, 243, 103, 3, 114, 4, 243, 164, 102, 97, 144, + 31, 7, 195, 102, 80, 103, 102, 141, 83, 16, 102, 133, 192, 15, 133, 10, + 0, 103, 102, 139, 74, 8, 102, 65, 235, 17, 144, 103, 102, 139, 66, 24, +102, 51, 210, 102, 247, 54, 82, 2, 102, 139, 200, 102, 43, 192, 102, 94, +232, 1, 0, 195, 6, 30, 102, 96, 103, 128, 123, 8, 1, 15, 132, 3, + 0, 233, 107, 251, 102, 131, 249, 0, 15, 133, 6, 0, 102, 97, 144, 31, + 7, 195, 102, 83, 102, 80, 102, 81, 102, 86, 102, 87, 6, 232, 145, 4, +102, 139, 209, 7, 102, 95, 102, 94, 102, 89, 102, 133, 192, 15, 132, 52, + 0, 102, 59, 202, 15, 141, 3, 0, 102, 139, 209, 232, 130, 254, 102, 43, +202, 102, 139, 218, 102, 139, 194, 102, 15, 182, 22, 13, 0, 102, 247, 226, +102, 15, 183, 22, 11, 0, 102, 247, 226, 102, 3, 248, 102, 88, 102, 3, +195, 102, 91, 235, 159, 102, 133, 246, 15, 132, 3, 251, 102, 81, 102, 87, + 6, 103, 102, 15, 182, 67, 9, 102, 133, 192, 15, 132, 32, 0, 102, 209, +224, 102, 43, 224, 102, 139, 252, 102, 84, 102, 86, 103, 102, 15, 183, 115, + 10, 102, 3, 243, 102, 139, 200, 243, 164, 102, 94, 235, 3, 144, 102, 80, +102, 80, 103, 102, 139, 3, 102, 80, 103, 102, 139, 67, 24, 102, 80, 103, +102, 139, 86, 32, 102, 133, 210, 15, 132, 11, 0, 102, 139, 254, 30, 7, +102, 139, 194, 232, 113, 3, 102, 139, 198, 102, 90, 102, 89, 102, 66, 102, + 81, 102, 86, 232, 63, 6, 102, 133, 192, 15, 132, 146, 250, 102, 94, 102, + 89, 102, 139, 254, 30, 7, 232, 78, 3, 102, 139, 198, 102, 139, 217, 102, + 89, 102, 90, 102, 81, 102, 86, 102, 209, 233, 232, 248, 253, 102, 133, 192, + 15, 132, 107, 250, 102, 94, 102, 89, 102, 3, 225, 7, 102, 95, 102, 89, +102, 139, 208, 102, 88, 102, 91, 102, 139, 218, 233, 245, 254, 6, 30, 102, + 96, 38, 103, 102, 15, 183, 95, 4, 38, 103, 102, 15, 183, 79, 6, 102, + 11, 201, 15, 132, 57, 250, 102, 3, 223, 102, 131, 195, 2, 102, 129, 199, +254, 1, 0, 0, 102, 73, 102, 11, 201, 15, 132, 23, 0, 38, 103, 139, + 3, 38, 103, 137, 7, 102, 131, 195, 2, 102, 129, 199, 0, 2, 0, 0, +102, 73, 235, 226, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 184, + 1, 0, 0, 0, 102, 163, 34, 2, 102, 161, 30, 2, 102, 3, 6, 102, + 2, 102, 163, 106, 2, 102, 3, 6, 102, 2, 102, 163, 78, 2, 102, 161, + 48, 0, 102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 139, 30, 78, 2, +102, 137, 7, 102, 163, 17, 0, 131, 195, 4, 102, 161, 86, 2, 102, 137, + 7, 163, 22, 0, 131, 195, 4, 102, 137, 30, 78, 2, 102, 139, 30, 30, + 2, 30, 7, 232, 92, 249, 102, 139, 251, 232, 81, 255, 102, 161, 30, 2, +102, 187, 32, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, + 0, 0, 232, 16, 253, 102, 11, 192, 15, 132, 25, 1, 102, 139, 216, 30, + 7, 102, 139, 62, 26, 2, 102, 51, 192, 232, 162, 253, 102, 139, 30, 26, + 2, 102, 129, 63, 128, 0, 0, 0, 15, 132, 235, 0, 3, 95, 4, 235, +240, 102, 83, 102, 139, 71, 16, 102, 247, 38, 86, 2, 102, 80, 102, 51, +210, 102, 15, 182, 30, 13, 0, 102, 247, 243, 102, 82, 232, 220, 0, 102, + 11, 192, 15, 132, 57, 249, 102, 139, 14, 86, 2, 102, 15, 182, 30, 13, + 0, 102, 247, 227, 102, 90, 102, 3, 194, 102, 139, 30, 78, 2, 102, 137, + 7, 131, 195, 4, 102, 15, 182, 6, 13, 0, 102, 43, 194, 102, 59, 193, + 15, 134, 3, 0, 102, 139, 193, 102, 137, 7, 102, 43, 200, 102, 90, 15, +132, 117, 0, 102, 3, 194, 102, 80, 102, 51, 210, 102, 15, 182, 30, 13, + 0, 102, 247, 243, 102, 81, 232, 130, 0, 102, 89, 102, 11, 192, 15, 132, +221, 248, 102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 139, 30, 78, 2, +102, 139, 23, 131, 195, 4, 102, 3, 23, 102, 59, 208, 15, 133, 21, 0, +102, 15, 182, 6, 13, 0, 102, 59, 193, 15, 134, 3, 0, 102, 139, 193, +102, 1, 7, 235, 165, 131, 195, 4, 102, 137, 30, 78, 2, 102, 137, 7, +131, 195, 4, 102, 15, 182, 6, 13, 0, 102, 59, 193, 15, 134, 3, 0, +102, 139, 193, 102, 137, 7, 235, 130, 131, 195, 4, 102, 255, 6, 34, 2, +102, 137, 30, 78, 2, 102, 91, 3, 95, 4, 102, 129, 63, 128, 0, 0, + 0, 15, 132, 12, 255, 102, 97, 144, 31, 7, 195, 102, 139, 208, 102, 139, + 14, 34, 2, 102, 139, 54, 106, 2, 102, 3, 54, 102, 2, 102, 82, 102, + 81, 102, 82, 102, 139, 30, 106, 2, 102, 139, 62, 86, 2, 102, 139, 4, +102, 163, 17, 0, 131, 198, 4, 102, 139, 4, 163, 22, 0, 131, 198, 4, + 30, 7, 232, 221, 247, 102, 43, 248, 15, 132, 8, 0, 247, 38, 11, 0, + 3, 216, 235, 217, 102, 139, 62, 106, 2, 30, 7, 232, 191, 253, 102, 161, +106, 2, 102, 187, 128, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 139, +209, 232, 129, 251, 102, 11, 192, 15, 132, 244, 247, 102, 139, 216, 102, 88, +102, 86, 232, 44, 1, 102, 94, 102, 11, 192, 15, 132, 5, 0, 102, 91, +102, 91, 195, 102, 89, 102, 90, 226, 132, 102, 51, 192, 195, 6, 30, 102, + 96, 102, 80, 102, 81, 102, 51, 210, 102, 15, 182, 30, 13, 0, 102, 247, +243, 102, 82, 102, 87, 232, 83, 255, 102, 95, 102, 11, 192, 15, 132, 174, +247, 102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 90, 102, 3, 194, 102, +163, 17, 0, 102, 89, 102, 15, 182, 30, 13, 0, 102, 59, 203, 15, 142, + 19, 0, 137, 30, 22, 0, 102, 43, 203, 102, 88, 102, 3, 195, 102, 80, +102, 81, 235, 20, 144, 102, 88, 102, 3, 193, 102, 80, 137, 14, 22, 0, +102, 185, 0, 0, 0, 0, 102, 81, 6, 102, 87, 139, 223, 131, 227, 15, +140, 192, 102, 193, 239, 4, 3, 199, 80, 7, 232, 5, 247, 102, 95, 7, +102, 3, 62, 82, 2, 102, 89, 102, 88, 102, 131, 249, 0, 15, 143, 112, +255, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 247, 38, 86, 2, +102, 139, 14, 86, 2, 232, 85, 255, 232, 210, 252, 102, 97, 144, 31, 7, +195, 6, 30, 102, 96, 102, 247, 38, 114, 2, 102, 139, 30, 54, 2, 102, +139, 14, 114, 2, 102, 139, 54, 42, 2, 30, 7, 102, 139, 62, 70, 2, +232, 129, 251, 232, 167, 252, 102, 97, 144, 31, 7, 195, 102, 80, 102, 83, +102, 81, 102, 139, 30, 74, 2, 102, 139, 200, 102, 193, 232, 3, 102, 131, +225, 7, 102, 3, 216, 102, 184, 1, 0, 0, 0, 102, 211, 224, 103, 132, + 3, 15, 132, 4, 0, 248, 235, 2, 144, 249, 102, 89, 102, 91, 102, 88, +195, 103, 128, 123, 8, 1, 15, 132, 4, 0, 102, 43, 192, 195, 103, 102, +141, 115, 16, 103, 102, 139, 86, 8, 102, 59, 194, 15, 135, 11, 0, 103, +102, 139, 22, 102, 59, 194, 15, 131, 4, 0, 102, 43, 192, 195, 103, 3, + 94, 16, 102, 43, 246, 103, 128, 59, 0, 15, 132, 62, 0, 232, 129, 0, +102, 3, 241, 232, 57, 0, 102, 3, 202, 102, 59, 193, 15, 140, 33, 0, +102, 139, 209, 102, 80, 103, 102, 15, 182, 11, 102, 139, 193, 102, 131, 224, + 15, 102, 193, 233, 4, 102, 3, 217, 102, 3, 216, 102, 67, 102, 88, 235, +196, 102, 43, 200, 102, 43, 194, 102, 3, 198, 195, 102, 43, 192, 195, 102, + 43, 201, 103, 138, 11, 128, 225, 15, 102, 131, 249, 0, 15, 133, 4, 0, +102, 43, 201, 195, 102, 83, 102, 82, 102, 3, 217, 103, 102, 15, 190, 19, +102, 73, 102, 75, 102, 131, 249, 0, 15, 132, 13, 0, 102, 193, 226, 8, +103, 138, 19, 102, 75, 102, 73, 235, 235, 102, 139, 202, 102, 90, 102, 91, +195, 102, 83, 102, 82, 102, 43, 210, 103, 138, 19, 102, 131, 226, 15, 102, + 43, 201, 103, 138, 11, 192, 233, 4, 102, 131, 249, 0, 15, 133, 8, 0, +102, 43, 201, 102, 90, 102, 91, 195, 102, 3, 218, 102, 3, 217, 103, 102, + 15, 190, 19, 102, 73, 102, 75, 102, 131, 249, 0, 15, 132, 13, 0, 102, +193, 226, 8, 103, 138, 19, 102, 75, 102, 73, 235, 235, 102, 139, 202, 102, + 90, 102, 91, 195, 102, 11, 201, 15, 133, 1, 0, 195, 102, 81, 102, 86, +103, 131, 62, 97, 15, 140, 12, 0, 103, 131, 62, 122, 15, 143, 4, 0, +103, 131, 46, 32, 102, 131, 198, 2, 226, 230, 102, 94, 102, 89, 195, 102, + 80, 102, 81, 102, 139, 208, 102, 161, 50, 2, 103, 102, 141, 88, 16, 103, + 3, 67, 4, 103, 102, 141, 64, 16, 102, 139, 218, 232, 68, 249, 102, 11, +192, 15, 132, 5, 0, 102, 89, 102, 89, 195, 102, 161, 54, 2, 102, 11, +192, 15, 133, 8, 0, 102, 89, 102, 89, 102, 51, 192, 195, 102, 139, 22, + 54, 2, 103, 102, 141, 82, 16, 103, 102, 139, 66, 24, 102, 51, 210, 102, +247, 54, 110, 2, 102, 51, 246, 102, 80, 102, 86, 102, 88, 102, 94, 102, + 59, 198, 15, 132, 58, 0, 102, 86, 102, 64, 102, 80, 102, 72, 232, 27, +254, 114, 232, 232, 235, 253, 102, 90, 102, 94, 102, 89, 102, 91, 102, 83, +102, 81, 102, 86, 102, 82, 102, 161, 70, 2, 103, 102, 141, 64, 24, 232, +208, 248, 102, 11, 192, 116, 196, 102, 89, 102, 89, 102, 89, 102, 89, 195, +102, 89, 102, 89, 102, 51, 192, 195, 102, 81, 102, 80, 102, 184, 5, 0, + 0, 0, 30, 7, 102, 139, 249, 232, 141, 253, 102, 139, 193, 102, 187, 32, + 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, + 51, 248, 102, 91, 102, 89, 102, 133, 192, 15, 133, 21, 0, 102, 139, 193, +102, 15, 183, 14, 16, 2, 102, 186, 18, 2, 0, 0, 232, 22, 248, 235, + 51, 144, 102, 51, 210, 102, 139, 193, 102, 139, 203, 102, 80, 102, 83, 232, + 35, 0, 102, 91, 102, 95, 102, 11, 192, 15, 132, 23, 0, 30, 7, 232, + 53, 253, 102, 139, 199, 102, 15, 183, 14, 16, 2, 102, 186, 18, 2, 0, + 0, 232, 225, 247, 195, 102, 82, 102, 81, 102, 187, 32, 0, 0, 0, 102, +185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 199, 247, 102, 11, +192, 15, 132, 99, 0, 102, 139, 216, 30, 7, 102, 139, 62, 26, 2, 102, + 51, 192, 232, 89, 248, 30, 7, 102, 139, 30, 26, 2, 102, 89, 102, 90, + 38, 102, 57, 15, 15, 133, 12, 0, 38, 102, 57, 87, 8, 15, 132, 49, + 0, 235, 19, 144, 38, 102, 131, 63, 255, 15, 132, 47, 0, 38, 131, 127, + 4, 0, 15, 132, 38, 0, 38, 102, 15, 183, 71, 4, 3, 216, 139, 195, + 37, 0, 128, 116, 203, 140, 192, 5, 0, 8, 142, 192, 129, 227, 255, 127, +235, 190, 38, 102, 139, 71, 16, 195, 102, 89, 102, 90, 102, 51, 192, 195, +102, 80, 102, 81, 102, 139, 199, 102, 193, 232, 4, 6, 89, 3, 200, 81, + 7, 102, 131, 231, 15, 102, 89, 102, 88, 195, 96, 6, 190, 189, 13, 191, + 0, 32, 30, 7, 185, 13, 0, 144, 243, 165, 7, 97, 195, 1, 35, 69, +103, 137, 171, 205, 239, 254, 220, 186, 152, 118, 84, 50, 16, 240, 225, 210, +195, 0, 0, 0, 0, 32, 32, 96, 139, 54, 24, 32, 38, 138, 5, 136, + 4, 71, 70, 102, 255, 6, 20, 32, 129, 254, 96, 32, 117, 6, 232, 91, + 0, 190, 32, 32, 226, 230, 137, 54, 24, 32, 97, 195, 102, 96, 139, 54, + 24, 32, 176, 128, 136, 4, 70, 50, 192, 129, 254, 96, 32, 117, 6, 232, + 58, 0, 190, 32, 32, 129, 254, 88, 32, 117, 233, 102, 51, 192, 102, 163, + 88, 32, 102, 161, 20, 32, 102, 193, 224, 3, 102, 15, 200, 102, 163, 92, + 32, 232, 24, 0, 187, 0, 32, 102, 139, 7, 102, 15, 200, 102, 137, 7, +131, 195, 4, 129, 251, 52, 32, 117, 238, 102, 97, 195, 102, 96, 187, 32, + 32, 102, 139, 7, 102, 15, 200, 102, 137, 7, 131, 195, 4, 129, 251, 96, + 32, 117, 238, 187, 0, 32, 102, 139, 15, 102, 139, 87, 4, 102, 139, 119, + 8, 102, 139, 127, 12, 102, 139, 111, 16, 187, 32, 32, 199, 6, 26, 32, + 48, 15, 198, 6, 28, 32, 20, 144, 83, 139, 30, 26, 32, 255, 23, 102, + 3, 71, 2, 91, 102, 3, 232, 102, 3, 47, 102, 139, 193, 102, 193, 192, + 5, 102, 3, 197, 102, 139, 239, 102, 139, 254, 102, 139, 242, 102, 193, 198, + 30, 102, 139, 209, 102, 139, 200, 102, 139, 7, 102, 51, 71, 8, 102, 51, + 71, 32, 102, 51, 71, 52, 102, 209, 192, 102, 137, 71, 64, 131, 195, 4, +254, 14, 28, 32, 117, 178, 131, 6, 26, 32, 6, 129, 62, 26, 32, 72, + 15, 117, 159, 187, 0, 32, 102, 1, 15, 102, 1, 87, 4, 102, 1, 119, + 8, 102, 1, 127, 12, 102, 1, 111, 16, 102, 97, 195, 102, 139, 198, 102, + 51, 199, 102, 35, 194, 102, 51, 199, 195, 102, 139, 194, 102, 51, 198, 102, + 51, 199, 195, 102, 83, 102, 139, 194, 102, 35, 198, 102, 139, 218, 102, 35, +223, 102, 11, 195, 102, 139, 222, 102, 35, 223, 102, 11, 195, 102, 91, 195, +252, 14, 153, 121, 130, 90, 9, 15, 161, 235, 217, 110, 19, 15, 220, 188, + 27, 143, 9, 15, 214, 193, 98, 202, 6, 30, 102, 96, 102, 51, 219, 184, + 0, 187, 205, 26, 102, 35, 192, 15, 133, 187, 0, 102, 129, 251, 84, 67, + 80, 65, 15, 133, 176, 0, 129, 249, 2, 1, 15, 130, 168, 0, 102, 97, +144, 31, 7, 6, 30, 102, 96, 103, 128, 123, 8, 0, 15, 133, 12, 0, +103, 102, 141, 83, 16, 103, 102, 139, 10, 235, 37, 144, 103, 102, 141, 83, + 16, 103, 102, 139, 74, 40, 102, 129, 249, 0, 0, 8, 0, 15, 131, 12, + 0, 103, 102, 139, 66, 44, 102, 35, 192, 15, 132, 3, 0, 102, 51, 201, + 14, 31, 232, 245, 253, 102, 35, 201, 15, 132, 50, 0, 102, 186, 0, 128, + 0, 0, 102, 59, 202, 15, 134, 31, 0, 102, 43, 202, 6, 102, 81, 102, + 87, 102, 82, 102, 139, 202, 232, 183, 253, 232, 251, 253, 102, 90, 102, 95, +102, 89, 7, 102, 3, 250, 235, 218, 232, 165, 253, 232, 233, 253, 232, 11, +254, 14, 7, 102, 187, 84, 67, 80, 65, 102, 191, 0, 32, 0, 0, 102, +185, 20, 0, 0, 0, 102, 184, 7, 187, 0, 0, 102, 186, 10, 0, 0, + 0, 102, 51, 246, 205, 26, 102, 97, 144, 31, 7, 195, 160, 249, 1, 233, + 64, 241, 160, 250, 1, 233, 58, 241 +}; diff --git a/ntfsprogs/boot.h b/ntfsprogs/boot.h new file mode 100755 index 0000000000000000000000000000000000000000..45d79927f4526d02715f12506f29e14af6f37b3e --- /dev/null +++ b/ntfsprogs/boot.h @@ -0,0 +1,7 @@ +#ifndef _NTFS_BOOT_H_ +#define _NTFS_BOOT_H_ + +extern const unsigned char boot_array[4136]; + +#endif /* _NTFS_BOOT_H_ */ + diff --git a/ntfsprogs/cluster.c b/ntfsprogs/cluster.c new file mode 100755 index 0000000000000000000000000000000000000000..82188f4cabf5a6e169fb48c5986c6d10a19e9588 --- /dev/null +++ b/ntfsprogs/cluster.c @@ -0,0 +1,132 @@ +/** + * cluster - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2003 Richard Russon + * Copyright (c) 2014 Jean-Pierre Andre + * + * This function will locate the owner of any given sector or cluster range. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "cluster.h" +#include "utils.h" +#include "logging.h" + +/** + * cluster_find + */ +int cluster_find(ntfs_volume *vol, LCN c_begin, LCN c_end, cluster_cb *cb, void *data) +{ + int j; + int result = -1; + struct mft_search_ctx *m_ctx = NULL; + ntfs_attr_search_ctx *a_ctx = NULL; + s64 count; + BOOL found; + ATTR_RECORD *rec; + runlist *runs; + + if (!vol || !cb) + return -1; + + m_ctx = mft_get_search_ctx(vol); + m_ctx->flags_search = FEMR_IN_USE | FEMR_BASE_RECORD; + count = 0; + + while (mft_next_record(m_ctx) == 0) { + + if (!(m_ctx->flags_match & FEMR_BASE_RECORD)) + continue; + + ntfs_log_verbose("Inode: %llu\n", (unsigned long long) + m_ctx->inode->mft_no); + + a_ctx = ntfs_attr_get_search_ctx(m_ctx->inode, NULL); + + found = FALSE; + while ((rec = find_attribute(AT_UNUSED, a_ctx))) { + + if (!rec->non_resident) { + ntfs_log_verbose("0x%02x skipped - attr is resident\n", + (int)le32_to_cpu(a_ctx->attr->type)); + continue; + } + + runs = ntfs_mapping_pairs_decompress(vol, a_ctx->attr, NULL); + if (!runs) { + ntfs_log_error("Couldn't read the data runs.\n"); + goto done; + } + + ntfs_log_verbose("\t[0x%02X]\n", + (int)le32_to_cpu(a_ctx->attr->type)); + + ntfs_log_verbose("\t\tVCN\tLCN\tLength\n"); + for (j = 0; runs[j].length > 0; j++) { + LCN a_begin = runs[j].lcn; + LCN a_end = a_begin + runs[j].length - 1; + + if (a_begin < 0) + continue; // sparse, discontiguous, etc + + ntfs_log_verbose("\t\t%lld\t%lld-%lld (%lld)\n", + (long long)runs[j].vcn, + (long long)runs[j].lcn, + (long long)(runs[j].lcn + + runs[j].length - 1), + (long long)runs[j].length); + //dprint list + + if ((a_begin > c_end) || (a_end < c_begin)) + continue; // before or after search range + + if ((*cb) (m_ctx->inode, a_ctx->attr, runs+j, data)) + return 1; + found = TRUE; + } + } + + ntfs_attr_put_search_ctx(a_ctx); + a_ctx = NULL; + if (found) + count++; + } + + if (count > 1) + ntfs_log_info("* %lld inodes found\n",(long long)count); + else + ntfs_log_info("* %s inode found\n", (count ? "one" : "no")); + result = 0; +done: + ntfs_attr_put_search_ctx(a_ctx); + mft_put_search_ctx(m_ctx); + + return result; +} + diff --git a/ntfsprogs/cluster.h b/ntfsprogs/cluster.h new file mode 100755 index 0000000000000000000000000000000000000000..4bc18276a54696317a5dd1513d17e55d0e5b7b3b --- /dev/null +++ b/ntfsprogs/cluster.h @@ -0,0 +1,39 @@ +/* + * cluster - Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Richard Russon + * + * This function will locate the owner of any given sector or cluster range. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CLUSTER_H_ +#define _CLUSTER_H_ + +#include "types.h" +#include "volume.h" + +typedef struct { + int x; +} ntfs_cluster; + +typedef int (cluster_cb)(ntfs_inode *ino, ATTR_RECORD *attr, runlist_element *run, void *data); + +int cluster_find(ntfs_volume *vol, LCN c_begin, LCN c_end, cluster_cb *cb, void *data); + +#endif /* _CLUSTER_H_ */ + diff --git a/ntfsprogs/list.h b/ntfsprogs/list.h new file mode 100755 index 0000000000000000000000000000000000000000..4db45d4ee589714a7c9972558184633855218dbd --- /dev/null +++ b/ntfsprogs/list.h @@ -0,0 +1,194 @@ +/* + * list.h - Linked list implementation. Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov and others + * + * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_LIST_H +#define _NTFS_LIST_H + +/** + * struct ntfs_list_head - Simple doubly linked list implementation. + * + * Copied from Linux kernel 2.4.2-ac18 into Linux-NTFS (with minor + * modifications). - AIA + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +struct ntfs_list_head { + struct ntfs_list_head *next, *prev; +}; + +#define NTFS_LIST_HEAD_INIT(name) { &(name), &(name) } + +#define NTFS_LIST_HEAD(name) \ + struct ntfs_list_head name = NTFS_LIST_HEAD_INIT(name) + +#define NTFS_INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/** + * __ntfs_list_add - Insert a new entry between two known consecutive entries. + * @new: + * @prev: + * @next: + * + * This is only for internal list manipulation where we know the prev/next + * entries already! + */ +static __inline__ void __ntfs_list_add(struct ntfs_list_head * new, + struct ntfs_list_head * prev, struct ntfs_list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * ntfs_list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static __inline__ void ntfs_list_add(struct ntfs_list_head *new, + struct ntfs_list_head *head) +{ + __ntfs_list_add(new, head, head->next); +} + +/** + * ntfs_list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static __inline__ void ntfs_list_add_tail(struct ntfs_list_head *new, + struct ntfs_list_head *head) +{ + __ntfs_list_add(new, head->prev, head); +} + +/** + * __ntfs_list_del - + * @prev: + * @next: + * + * Delete a list entry by making the prev/next entries point to each other. + * + * This is only for internal list manipulation where we know the prev/next + * entries already! + */ +static __inline__ void __ntfs_list_del(struct ntfs_list_head * prev, + struct ntfs_list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * ntfs_list_del - deletes entry from list. + * @entry: the element to delete from the list. + * + * Note: ntfs_list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static __inline__ void ntfs_list_del(struct ntfs_list_head *entry) +{ + __ntfs_list_del(entry->prev, entry->next); +} + +/** + * ntfs_list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline__ void ntfs_list_del_init(struct ntfs_list_head *entry) +{ + __ntfs_list_del(entry->prev, entry->next); + NTFS_INIT_LIST_HEAD(entry); +} + +/** + * ntfs_list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline__ int ntfs_list_empty(struct ntfs_list_head *head) +{ + return head->next == head; +} + +/** + * ntfs_list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static __inline__ void ntfs_list_splice(struct ntfs_list_head *list, + struct ntfs_list_head *head) +{ + struct ntfs_list_head *first = list->next; + + if (first != list) { + struct ntfs_list_head *last = list->prev; + struct ntfs_list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +/** + * ntfs_list_entry - get the struct for this entry + * @ptr: the &struct ntfs_list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define ntfs_list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * ntfs_list_for_each - iterate over a list + * @pos: the &struct ntfs_list_head to use as a loop counter. + * @head: the head for your list. + */ +#define ntfs_list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * ntfs_list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct ntfs_list_head to use as a loop counter. + * @n: another &struct ntfs_list_head to use as temporary storage + * @head: the head for your list. + */ +#define ntfs_list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +#endif /* defined _NTFS_LIST_H */ + diff --git a/ntfsprogs/mkntfs.8 b/ntfsprogs/mkntfs.8 new file mode 100755 index 0000000000000000000000000000000000000000..12005fcb6af310c10a6931fad127da4d0c45a564 --- /dev/null +++ b/ntfsprogs/mkntfs.8 @@ -0,0 +1,290 @@ +.\" Copyright (c) 2001\-2006 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" Copyright (c) 2005\-2006 Szabolcs Szakacsits. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH MKNTFS 8 "January 2006" "ntfs-3g 2015.3.14" +.SH NAME +mkntfs \- create an NTFS file system +.SH SYNOPSIS +.B mkntfs +[\fIoptions\fR] \fIdevice \fR[\fInumber\-of\-sectors\fR] +.PP +.B mkntfs +[ +.B \-C +] +[ +.B \-c +.I cluster\-size +] +[ +.B \-F +] +[ +.B \-f +] +[ +.B \-H +.I heads +] +[ +.B \-h +] +[ +.B \-I +] +[ +.B \-L +.I volume\-label +] +[ +.B \-l +] +[ +.B \-n +] +[ +.B \-p +.I part\-start\-sect +] +[ +.B \-Q +] +[ +.B \-q +] +[ +.B \-S +.I sectors\-per\-track +] +[ +.B \-s +.I sector\-size +] +[ +.B \-T +] +[ +.B \-U +] +[ +.B \-V +] +[ +.B \-v +] +[ +.B \-z +.I mft\-zone\-multiplier +] +[ +.B \-\-debug +] +.I device +[ +.I number\-of\-sectors +] +.SH DESCRIPTION +.B mkntfs +is used to create an NTFS file system on a device (usually a disk partition) +or file. +.I device +is the special file corresponding to the device (e.g +.IR /dev/hdXX ). +.I number\-of\-sectors +is the number of sectors on the device. If omitted, +.B mkntfs +automagically figures the file system size. +.SH OPTIONS +Below is a summary of all the options that +.B mkntfs +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.SS Basic options +.TP +\fB\-f\fR, \fB\-\-fast\fR, \fB\-Q\fR, \fB\-\-quick\fR +Perform quick (fast) format. This will skip both zeroing of the volume and bad +sector checking. +.TP +\fB\-L\fR, \fB\-\-label\fR STRING +Set the volume label for the filesystem. +.TP +\fB\-C\fR, \fB\-\-enable\-compression\fR +Enable compression on the volume. +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Causes +.B mkntfs +to not actually create a filesystem, but display what it would do if it were +to create a filesystem. All steps of the format are carried out except the +actual writing to the device. +.SS Advanced options +.TP +\fB\-c\fR, \fB\-\-cluster\-size\fR BYTES +Specify the size of clusters in bytes. Valid cluster size values are powers of +two, with at least 256, and at most 65536 bytes per cluster. If omitted, +.B mkntfs +uses 4096 bytes as the default cluster size. +.sp +Note that the default cluster size is set to be at least equal to the sector +size as a cluster cannot be smaller than a sector. Also, note that values +greater than 4096 have the side effect that compression is disabled on the +volume (due to limitations in the NTFS compression algorithm currently in use +by Windows). +.TP +\fB\-s\fR, \fB\-\-sector\-size\fR BYTES +Specify the size of sectors in bytes. Valid sector size values are 256, 512, +1024, 2048 and 4096 bytes per sector. If omitted, +.B mkntfs +attempts to determine the +.I sector\-size +automatically and if that fails a default of 512 bytes per sector is used. +.TP +\fB\-p\fR, \fB\-\-partition\-start\fR SECTOR +Specify the partition start sector. The maximum is 4294967295 (2^32\-1). If +omitted, +.B mkntfs +attempts to determine +.I part\-start\-sect +automatically and if that fails a default of 0 is used. Note that +.I part\-start\-sect +is required for Windows to be able to boot from the created volume. +.TP +\fB\-H\fR, \fB\-\-heads\fR NUM +Specify the number of heads. The maximum is 65535 (0xffff). If omitted, +.B mkntfs +attempts to determine the number of +.I heads +automatically and if that fails a default of 0 is used. Note that +.I heads +is required for Windows to be able to boot from the created volume. +.TP +\fB\-S\fR, \fB\-\-sectors\-per\-track\fR NUM +Specify the number of sectors per track. The maximum is 65535 (0xffff). If +omitted, +.B mkntfs +attempts to determine the number of +.I sectors\-per\-track +automatically and if that fails a default of 0 is used. Note that +.I sectors\-per\-track +is required for Windows to be able to boot from the created volume. +.TP +\fB\-z\fR, \fB\-\-mft\-zone\-multiplier\fR NUM +Set the MFT zone multiplier, which determines the size of the MFT zone to use +on the volume. The MFT zone is the area at the beginning of the volume reserved +for the master file table (MFT), which stores the on disk inodes (MFT records). +It is noteworthy that small files are stored entirely within the inode; +thus, if you expect to use the volume for storing large numbers of very small +files, it is useful to set the zone multiplier to a higher value. Note, that +the MFT zone is resized on the fly as required during operation of the NTFS +driver but choosing a good value will reduce fragmentation. Valid values +are 1, 2, 3 and 4. The values have the following meaning: +.TS +box; +lB lB +lB lB +c l. +MFT zone MFT zone size +multiplier (% of volume size) +1 12.5% (default) +2 25.0% +3 37.5% +4 50.0% +.TE +.sp +.TP +\fB\-T\fR, \fB\-\-zero\-time\fR +Fake the time to be 00:00:00 UTC, Jan 1, 1970 instead of the current system +time. This is only really useful for debugging purposes. +.TP +\fB\-U\fR, \fB\-\-with\-uuid\fR +Generate a random volume UUID. +.TP +\fB\-I\fR, \fB\-\-no\-indexing\fR +Disable content indexing on the volume. (This is only meaningful on +Windows 2000 and later. Windows NT 4.0 and earlier ignore this as they do +not implement content indexing at all.) +.TP +\fB\-F\fR, \fB\-\-force\fR +Force +.B mkntfs +to run, even if the specified +.I device +is not a block special device, or appears to be mounted. +.SS Output options +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Quiet execution; only errors are written to stderr, no output to stdout +occurs at all. Useful if +.B mkntfs +is run in a script. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Verbose execution. +.TP +\fB\-\-debug\fR +Really verbose execution; includes the verbose output from the +.B \-v +option as well as additional output useful for debugging +.B mkntfs. +.SS Help options +.TP +\fB\-V\fR, \fB\-\-version\fR +Print the version number of +.B mkntfs +and exit. +.TP +\fB\-l\fR, \fB\-\-license\fR +Print the licensing information of +.B mkntfs +and exit. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.SH KNOWN ISSUES +When applying chkdsk to a file system, it sometimes throws a warning +"Correcting errors in the uppercase file." The uppercase file is created +while formatting and it defines the mapping of lower case characters to +upper case ones, as needed to sort file names in directories. The warning +means that the uppercase file defined on the file system is not the same as +the one used by the Windows OS on which chkdsk is running, and this may +happen because newer versions of Windows take into account new characters +defined by the Unicode consortium. +.P +Currently, mkntfs creates the uppercase table so that no warning is thrown +by Windows Vista, Windows 7 or Windows 8. A warning may be thrown by +other Windows versions, or if chkdsk is applied in succession on different +Windows versions. +.SH BUGS +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B mkntfs +was written by Anton Altaparmakov, Richard Russon, Erik Sornes and Szabolcs Szakacsits. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH AVAILABILITY +.B mkntfs +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR badblocks (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/mkntfs.8.in b/ntfsprogs/mkntfs.8.in new file mode 100755 index 0000000000000000000000000000000000000000..ce687f7dc6f2c779a26be9e7322f881ae407ab00 --- /dev/null +++ b/ntfsprogs/mkntfs.8.in @@ -0,0 +1,290 @@ +.\" Copyright (c) 2001\-2006 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" Copyright (c) 2005\-2006 Szabolcs Szakacsits. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH MKNTFS 8 "January 2006" "ntfs-3g @VERSION@" +.SH NAME +mkntfs \- create an NTFS file system +.SH SYNOPSIS +.B mkntfs +[\fIoptions\fR] \fIdevice \fR[\fInumber\-of\-sectors\fR] +.PP +.B mkntfs +[ +.B \-C +] +[ +.B \-c +.I cluster\-size +] +[ +.B \-F +] +[ +.B \-f +] +[ +.B \-H +.I heads +] +[ +.B \-h +] +[ +.B \-I +] +[ +.B \-L +.I volume\-label +] +[ +.B \-l +] +[ +.B \-n +] +[ +.B \-p +.I part\-start\-sect +] +[ +.B \-Q +] +[ +.B \-q +] +[ +.B \-S +.I sectors\-per\-track +] +[ +.B \-s +.I sector\-size +] +[ +.B \-T +] +[ +.B \-U +] +[ +.B \-V +] +[ +.B \-v +] +[ +.B \-z +.I mft\-zone\-multiplier +] +[ +.B \-\-debug +] +.I device +[ +.I number\-of\-sectors +] +.SH DESCRIPTION +.B mkntfs +is used to create an NTFS file system on a device (usually a disk partition) +or file. +.I device +is the special file corresponding to the device (e.g +.IR /dev/hdXX ). +.I number\-of\-sectors +is the number of sectors on the device. If omitted, +.B mkntfs +automagically figures the file system size. +.SH OPTIONS +Below is a summary of all the options that +.B mkntfs +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.SS Basic options +.TP +\fB\-f\fR, \fB\-\-fast\fR, \fB\-Q\fR, \fB\-\-quick\fR +Perform quick (fast) format. This will skip both zeroing of the volume and bad +sector checking. +.TP +\fB\-L\fR, \fB\-\-label\fR STRING +Set the volume label for the filesystem. +.TP +\fB\-C\fR, \fB\-\-enable\-compression\fR +Enable compression on the volume. +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Causes +.B mkntfs +to not actually create a filesystem, but display what it would do if it were +to create a filesystem. All steps of the format are carried out except the +actual writing to the device. +.SS Advanced options +.TP +\fB\-c\fR, \fB\-\-cluster\-size\fR BYTES +Specify the size of clusters in bytes. Valid cluster size values are powers of +two, with at least 256, and at most 65536 bytes per cluster. If omitted, +.B mkntfs +uses 4096 bytes as the default cluster size. +.sp +Note that the default cluster size is set to be at least equal to the sector +size as a cluster cannot be smaller than a sector. Also, note that values +greater than 4096 have the side effect that compression is disabled on the +volume (due to limitations in the NTFS compression algorithm currently in use +by Windows). +.TP +\fB\-s\fR, \fB\-\-sector\-size\fR BYTES +Specify the size of sectors in bytes. Valid sector size values are 256, 512, +1024, 2048 and 4096 bytes per sector. If omitted, +.B mkntfs +attempts to determine the +.I sector\-size +automatically and if that fails a default of 512 bytes per sector is used. +.TP +\fB\-p\fR, \fB\-\-partition\-start\fR SECTOR +Specify the partition start sector. The maximum is 4294967295 (2^32\-1). If +omitted, +.B mkntfs +attempts to determine +.I part\-start\-sect +automatically and if that fails a default of 0 is used. Note that +.I part\-start\-sect +is required for Windows to be able to boot from the created volume. +.TP +\fB\-H\fR, \fB\-\-heads\fR NUM +Specify the number of heads. The maximum is 65535 (0xffff). If omitted, +.B mkntfs +attempts to determine the number of +.I heads +automatically and if that fails a default of 0 is used. Note that +.I heads +is required for Windows to be able to boot from the created volume. +.TP +\fB\-S\fR, \fB\-\-sectors\-per\-track\fR NUM +Specify the number of sectors per track. The maximum is 65535 (0xffff). If +omitted, +.B mkntfs +attempts to determine the number of +.I sectors\-per\-track +automatically and if that fails a default of 0 is used. Note that +.I sectors\-per\-track +is required for Windows to be able to boot from the created volume. +.TP +\fB\-z\fR, \fB\-\-mft\-zone\-multiplier\fR NUM +Set the MFT zone multiplier, which determines the size of the MFT zone to use +on the volume. The MFT zone is the area at the beginning of the volume reserved +for the master file table (MFT), which stores the on disk inodes (MFT records). +It is noteworthy that small files are stored entirely within the inode; +thus, if you expect to use the volume for storing large numbers of very small +files, it is useful to set the zone multiplier to a higher value. Note, that +the MFT zone is resized on the fly as required during operation of the NTFS +driver but choosing a good value will reduce fragmentation. Valid values +are 1, 2, 3 and 4. The values have the following meaning: +.TS +box; +lB lB +lB lB +c l. +MFT zone MFT zone size +multiplier (% of volume size) +1 12.5% (default) +2 25.0% +3 37.5% +4 50.0% +.TE +.sp +.TP +\fB\-T\fR, \fB\-\-zero\-time\fR +Fake the time to be 00:00:00 UTC, Jan 1, 1970 instead of the current system +time. This is only really useful for debugging purposes. +.TP +\fB\-U\fR, \fB\-\-with\-uuid\fR +Generate a random volume UUID. +.TP +\fB\-I\fR, \fB\-\-no\-indexing\fR +Disable content indexing on the volume. (This is only meaningful on +Windows 2000 and later. Windows NT 4.0 and earlier ignore this as they do +not implement content indexing at all.) +.TP +\fB\-F\fR, \fB\-\-force\fR +Force +.B mkntfs +to run, even if the specified +.I device +is not a block special device, or appears to be mounted. +.SS Output options +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Quiet execution; only errors are written to stderr, no output to stdout +occurs at all. Useful if +.B mkntfs +is run in a script. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Verbose execution. +.TP +\fB\-\-debug\fR +Really verbose execution; includes the verbose output from the +.B \-v +option as well as additional output useful for debugging +.B mkntfs. +.SS Help options +.TP +\fB\-V\fR, \fB\-\-version\fR +Print the version number of +.B mkntfs +and exit. +.TP +\fB\-l\fR, \fB\-\-license\fR +Print the licensing information of +.B mkntfs +and exit. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.SH KNOWN ISSUES +When applying chkdsk to a file system, it sometimes throws a warning +"Correcting errors in the uppercase file." The uppercase file is created +while formatting and it defines the mapping of lower case characters to +upper case ones, as needed to sort file names in directories. The warning +means that the uppercase file defined on the file system is not the same as +the one used by the Windows OS on which chkdsk is running, and this may +happen because newer versions of Windows take into account new characters +defined by the Unicode consortium. +.P +Currently, mkntfs creates the uppercase table so that no warning is thrown +by Windows Vista, Windows 7 or Windows 8. A warning may be thrown by +other Windows versions, or if chkdsk is applied in succession on different +Windows versions. +.SH BUGS +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B mkntfs +was written by Anton Altaparmakov, Richard Russon, Erik Sornes and Szabolcs Szakacsits. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH AVAILABILITY +.B mkntfs +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR badblocks (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/mkntfs.c b/ntfsprogs/mkntfs.c new file mode 100755 index 0000000000000000000000000000000000000000..a7b1fd235fe9d89f8676fa05b88e256009da0650 --- /dev/null +++ b/ntfsprogs/mkntfs.c @@ -0,0 +1,5180 @@ +/** + * mkntfs - Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2011 Anton Altaparmakov + * Copyright (c) 2001-2005 Richard Russon + * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2005 Erik Sornes + * Copyright (c) 2007 Yura Pakhuchiy + * Copyright (c) 2010-2014 Jean-Pierre Andre + * + * This utility will create an NTFS 1.2 or 3.1 volume on a user + * specified (block) device. + * + * Some things (option handling and determination of mount status) have been + * adapted from e2fsprogs-1.19 and lib/ext2fs/ismounted.c and misc/mke2fs.c in + * particular. + * + * 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 (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_LIBGEN_H +#include <libgen.h> +#endif +#ifdef ENABLE_UUID +#include <uuid/uuid.h> +#endif + + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else + extern char *optarg; + extern int optind; +#endif + +#ifdef HAVE_LINUX_MAJOR_H +# include <linux/major.h> +# ifndef MAJOR +# define MAJOR(dev) ((dev) >> 8) +# define MINOR(dev) ((dev) & 0xff) +# endif +# ifndef IDE_DISK_MAJOR +# ifndef IDE0_MAJOR +# define IDE0_MAJOR 3 +# define IDE1_MAJOR 22 +# define IDE2_MAJOR 33 +# define IDE3_MAJOR 34 +# define IDE4_MAJOR 56 +# define IDE5_MAJOR 57 +# define IDE6_MAJOR 88 +# define IDE7_MAJOR 89 +# define IDE8_MAJOR 90 +# define IDE9_MAJOR 91 +# endif +# define IDE_DISK_MAJOR(M) \ + ((M) == IDE0_MAJOR || (M) == IDE1_MAJOR || \ + (M) == IDE2_MAJOR || (M) == IDE3_MAJOR || \ + (M) == IDE4_MAJOR || (M) == IDE5_MAJOR || \ + (M) == IDE6_MAJOR || (M) == IDE7_MAJOR || \ + (M) == IDE8_MAJOR || (M) == IDE9_MAJOR) +# endif +# ifndef SCSI_DISK_MAJOR +# ifndef SCSI_DISK0_MAJOR +# define SCSI_DISK0_MAJOR 8 +# define SCSI_DISK1_MAJOR 65 +# define SCSI_DISK7_MAJOR 71 +# endif +# define SCSI_DISK_MAJOR(M) \ + ((M) == SCSI_DISK0_MAJOR || \ + ((M) >= SCSI_DISK1_MAJOR && \ + (M) <= SCSI_DISK7_MAJOR)) +# endif +#endif + +#include "security.h" +#include "types.h" +#include "attrib.h" +#include "bitmap.h" +#include "bootsect.h" +#include "device.h" +#include "dir.h" +#include "mft.h" +#include "mst.h" +#include "runlist.h" +#include "utils.h" +#include "ntfstime.h" +#include "sd.h" +#include "boot.h" +#include "attrdef.h" +/* #include "version.h" */ +#include "logging.h" +#include "support.h" +#include "unistr.h" +#include "misc.h" + +#if defined(__sun) && defined (__SVR4) +#undef basename +#define basename(name) name +#endif + +typedef enum { WRITE_STANDARD, WRITE_BITMAP, WRITE_LOGFILE } WRITE_TYPE; + +#ifdef NO_NTFS_DEVICE_DEFAULT_IO_OPS +#error "No default device io operations! Cannot build mkntfs. \ +You need to run ./configure without the --disable-default-device-io-ops \ +switch if you want to be able to build the NTFS utilities." +#endif + +/* Page size on ia32. Can change to 8192 on Alpha. */ +#define NTFS_PAGE_SIZE 4096 + +static char EXEC_NAME[] = "mkntfs"; + +struct BITMAP_ALLOCATION { + struct BITMAP_ALLOCATION *next; + LCN lcn; /* first allocated cluster */ + s64 length; /* count of consecutive clusters */ +} ; + + /* Upcase $Info, used since Windows 8 */ +struct UPCASEINFO { + le32 len; + le32 filler; + le64 crc; + le32 osmajor; + le32 osminor; + le32 build; + le16 packmajor; + le16 packminor; +} ; + +/** + * global variables + */ +static u8 *g_buf = NULL; +static int g_mft_bitmap_byte_size = 0; +static u8 *g_mft_bitmap = NULL; +static int g_lcn_bitmap_byte_size = 0; +static int g_dynamic_buf_size = 0; +static u8 *g_dynamic_buf = NULL; +static struct UPCASEINFO *g_upcaseinfo = NULL; +static runlist *g_rl_mft = NULL; +static runlist *g_rl_mft_bmp = NULL; +static runlist *g_rl_mftmirr = NULL; +static runlist *g_rl_logfile = NULL; +static runlist *g_rl_boot = NULL; +static runlist *g_rl_bad = NULL; +static INDEX_ALLOCATION *g_index_block = NULL; +static ntfs_volume *g_vol = NULL; +static int g_mft_size = 0; +static long long g_mft_lcn = 0; /* lcn of $MFT, $DATA attribute */ +static long long g_mftmirr_lcn = 0; /* lcn of $MFTMirr, $DATA */ +static long long g_logfile_lcn = 0; /* lcn of $LogFile, $DATA */ +static int g_logfile_size = 0; /* in bytes, determined from volume_size */ +static long long g_mft_zone_end = 0; /* Determined from volume_size and mft_zone_multiplier, in clusters */ +static long long g_num_bad_blocks = 0; /* Number of bad clusters */ +static long long *g_bad_blocks = NULL; /* Array of bad clusters */ + +static struct BITMAP_ALLOCATION *g_allocation = NULL; /* Head of cluster allocations */ + +/** + * struct mkntfs_options + */ +static struct mkntfs_options { + char *dev_name; /* Name of the device, or file, to use */ + BOOL enable_compression; /* -C, enables compression of all files on the volume by default. */ + BOOL quick_format; /* -f or -Q, fast format, don't zero the volume first. */ + BOOL force; /* -F, force fs creation. */ + long heads; /* -H, number of heads on device */ + BOOL disable_indexing; /* -I, disables indexing of file contents on the volume by default. */ + BOOL no_action; /* -n, do not write to device, only display what would be done. */ + long long part_start_sect; /* -p, start sector of partition on parent device */ + long sector_size; /* -s, in bytes, power of 2, default is 512 bytes. */ + long sectors_per_track; /* -S, number of sectors per track on device */ + BOOL use_epoch_time; /* -T, fake the time to be 00:00:00 UTC, Jan 1, 1970. */ + long mft_zone_multiplier; /* -z, value from 1 to 4. Default is 1. */ + long long num_sectors; /* size of device in sectors */ + long cluster_size; /* -c, format with this cluster-size */ + BOOL with_uuid; /* -U, request setting an uuid */ + char *label; /* -L, volume label */ +} opts; + + +/** + * mkntfs_license + */ +static void mkntfs_license(void) +{ + ntfs_log_info("%s", ntfs_gpl); +} + +/** + * mkntfs_usage + */ +static void mkntfs_usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device [number-of-sectors]\n" +"\n" +"Basic options:\n" +" -f, --fast Perform a quick format\n" +" -Q, --quick Perform a quick format\n" +" -L, --label STRING Set the volume label\n" +" -C, --enable-compression Enable compression on the volume\n" +" -I, --no-indexing Disable indexing on the volume\n" +" -n, --no-action Do not write to disk\n" +"\n" +"Advanced options:\n" +" -c, --cluster-size BYTES Specify the cluster size for the volume\n" +" -s, --sector-size BYTES Specify the sector size for the device\n" +" -p, --partition-start SECTOR Specify the partition start sector\n" +" -H, --heads NUM Specify the number of heads\n" +" -S, --sectors-per-track NUM Specify the number of sectors per track\n" +" -z, --mft-zone-multiplier NUM Set the MFT zone multiplier\n" +" -T, --zero-time Fake the time to be 00:00 UTC, Jan 1, 1970\n" +" -F, --force Force execution despite errors\n" +"\n" +"Output options:\n" +" -q, --quiet Quiet execution\n" +" -v, --verbose Verbose execution\n" +" --debug Very verbose execution\n" +"\n" +"Help options:\n" +" -V, --version Display version\n" +" -l, --license Display licensing information\n" +" -h, --help Display this help\n" +"\n", basename(EXEC_NAME)); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * mkntfs_version + */ +static void mkntfs_version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g)\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Create an NTFS volume on a user specified (block) " + "device.\n\n"); + ntfs_log_info("Copyright (c) 2000-2007 Anton Altaparmakov\n"); + ntfs_log_info("Copyright (c) 2001-2005 Richard Russon\n"); + ntfs_log_info("Copyright (c) 2002-2006 Szabolcs Szakacsits\n"); + ntfs_log_info("Copyright (c) 2005 Erik Sornes\n"); + ntfs_log_info("Copyright (c) 2007 Yura Pakhuchiy\n"); + ntfs_log_info("Copyright (c) 2010-2014 Jean-Pierre Andre\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/* + * crc64, adapted from http://rpm5.org/docs/api/digest_8c-source.html + * ECMA-182 polynomial, see + * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-182.pdf + */ + /* make sure the needed types are defined */ +#undef byte +#undef uint32_t +#undef uint64_t +#define byte u8 +#define uint32_t u32 +#define uint64_t u64 +static uint64_t crc64(uint64_t crc, const byte * data, size_t size) + /*@*/ +{ + static uint64_t polynomial = 0x9a6c9329ac4bc9b5ULL; + static uint64_t xorout = 0xffffffffffffffffULL; + static uint64_t table[256]; + + crc ^= xorout; + + if (data == NULL) { + /* generate the table of CRC remainders for all possible bytes */ + uint64_t c; + uint32_t i, j; + for (i = 0; i < 256; i++) { + c = i; + for (j = 0; j < 8; j++) { + if (c & 1) + c = polynomial ^ (c >> 1); + else + c = (c >> 1); + } + table[i] = c; + } + } else + while (size) { + crc = table[(crc ^ *data) & 0xff] ^ (crc >> 8); + size--; + data++; + } + + crc ^= xorout; + + return crc; +} + +/* + * Mark a run of clusters as allocated + * + * Returns FALSE if unsuccessful + */ + +static BOOL bitmap_allocate(LCN lcn, s64 length) +{ + BOOL done; + struct BITMAP_ALLOCATION *p; + struct BITMAP_ALLOCATION *q; + struct BITMAP_ALLOCATION *newall; + + done = TRUE; + if (length) { + p = g_allocation; + q = (struct BITMAP_ALLOCATION*)NULL; + /* locate the first run which starts beyond the requested lcn */ + while (p && (p->lcn <= lcn)) { + q = p; + p = p->next; + } + /* make sure the requested lcns were not allocated */ + if ((q && ((q->lcn + q->length) > lcn)) + || (p && ((lcn + length) > p->lcn))) { + ntfs_log_error("Bitmap allocation error\n"); + done = FALSE; + } + if (q && ((q->lcn + q->length) == lcn)) { + /* extend current run, no overlapping possible */ + q->length += length; + } else { + newall = (struct BITMAP_ALLOCATION*) + ntfs_malloc(sizeof(struct BITMAP_ALLOCATION)); + if (newall) { + newall->lcn = lcn; + newall->length = length; + newall->next = p; + if (q) q->next = newall; + else g_allocation = newall; + } else { + done = FALSE; + ntfs_log_perror("Not enough memory"); + } + } + } + return (done); +} + +/* + * Mark a run of cluster as not allocated + * + * Returns FALSE if unsuccessful + * (freeing free clusters is not considered as an error) + */ + +static BOOL bitmap_deallocate(LCN lcn, s64 length) +{ + BOOL done; + struct BITMAP_ALLOCATION *p; + struct BITMAP_ALLOCATION *q; + LCN first, last; + s64 begin_length, end_length; + + done = TRUE; + if (length) { + p = g_allocation; + q = (struct BITMAP_ALLOCATION*)NULL; + /* locate a run which has a common portion */ + while (p) { + first = (p->lcn > lcn ? p->lcn : lcn); + last = ((p->lcn + p->length) < (lcn + length) + ? p->lcn + p->length : lcn + length); + if (first < last) { + /* get the parts which must be kept */ + begin_length = first - p->lcn; + end_length = p->lcn + p->length - last; + /* delete the entry */ + if (q) + q->next = p->next; + else + g_allocation = p->next; + free(p); + /* reallocate the beginning and the end */ + if (begin_length + && !bitmap_allocate(first - begin_length, + begin_length)) + done = FALSE; + if (end_length + && !bitmap_allocate(last, end_length)) + done = FALSE; + /* restart a full search */ + p = g_allocation; + q = (struct BITMAP_ALLOCATION*)NULL; + } else { + q = p; + p = p->next; + } + } + } + return (done); +} + +/* + * Get the allocation status of a single cluster + * and mark as allocated + * + * Returns 1 if the cluster was previously allocated + */ + +static int bitmap_get_and_set(LCN lcn, unsigned long length) +{ + struct BITMAP_ALLOCATION *p; + struct BITMAP_ALLOCATION *q; + int bit; + + if (length == 1) { + p = g_allocation; + q = (struct BITMAP_ALLOCATION*)NULL; + /* locate the first run which starts beyond the requested lcn */ + while (p && (p->lcn <= lcn)) { + q = p; + p = p->next; + } + if (q && (q->lcn <= lcn) && ((q->lcn + q->length) > lcn)) + bit = 1; /* was allocated */ + else { + bitmap_allocate(lcn, length); + bit = 0; + } + } else { + ntfs_log_error("Can only allocate a single cluster at a time\n"); + bit = 0; + } + return (bit); +} + +/* + * Build a section of the bitmap according to allocation + */ + +static void bitmap_build(u8 *buf, LCN lcn, s64 length) +{ + struct BITMAP_ALLOCATION *p; + LCN first, last; + int j; /* byte number */ + int bn; /* bit number */ + + for (j=0; (8*j)<length; j++) + buf[j] = 0; + for (p=g_allocation; p; p=p->next) { + first = (p->lcn > lcn ? p->lcn : lcn); + last = ((p->lcn + p->length) < (lcn + length) + ? p->lcn + p->length : lcn + length); + if (first < last) { + bn = first - lcn; + /* initial partial byte, if any */ + while ((bn < (last - lcn)) && (bn & 7)) { + buf[bn >> 3] |= 1 << (bn & 7); + bn++; + } + /* full bytes */ + while (bn < (last - lcn - 7)) { + buf[bn >> 3] = 255; + bn += 8; + } + /* final partial byte, if any */ + while (bn < (last - lcn)) { + buf[bn >> 3] |= 1 << (bn & 7); + bn++; + } + } + } +} + +/** + * mkntfs_parse_long + */ +static BOOL mkntfs_parse_long(const char *string, const char *name, long *num) +{ + char *end = NULL; + long tmp; + + if (!string || !name || !num) + return FALSE; + + if (*num >= 0) { + ntfs_log_error("You may only specify the %s once.\n", name); + return FALSE; + } + + tmp = strtol(string, &end, 0); + if (end && *end) { + ntfs_log_error("Cannot understand the %s '%s'.\n", name, string); + return FALSE; + } else { + *num = tmp; + return TRUE; + } +} + +/** + * mkntfs_parse_llong + */ +static BOOL mkntfs_parse_llong(const char *string, const char *name, + long long *num) +{ + char *end = NULL; + long long tmp; + + if (!string || !name || !num) + return FALSE; + + if (*num >= 0) { + ntfs_log_error("You may only specify the %s once.\n", name); + return FALSE; + } + + tmp = strtoll(string, &end, 0); + if (end && *end) { + ntfs_log_error("Cannot understand the %s '%s'.\n", name, + string); + return FALSE; + } else { + *num = tmp; + return TRUE; + } +} + +/** + * mkntfs_init_options + */ +static void mkntfs_init_options(struct mkntfs_options *opts2) +{ + if (!opts2) + return; + + memset(opts2, 0, sizeof(*opts2)); + + /* Mark all the numeric options as "unset". */ + opts2->cluster_size = -1; + opts2->heads = -1; + opts2->mft_zone_multiplier = -1; + opts2->num_sectors = -1; + opts2->part_start_sect = -1; + opts2->sector_size = -1; + opts2->sectors_per_track = -1; +} + +/** + * mkntfs_parse_options + */ +static int mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options *opts2) +{ + static const char *sopt = "-c:CfFhH:IlL:np:qQs:S:TUvVz:"; + static const struct option lopt[] = { + { "cluster-size", required_argument, NULL, 'c' }, + { "debug", no_argument, NULL, 'Z' }, + { "enable-compression", no_argument, NULL, 'C' }, + { "fast", no_argument, NULL, 'f' }, + { "force", no_argument, NULL, 'F' }, + { "heads", required_argument, NULL, 'H' }, + { "help", no_argument, NULL, 'h' }, + { "label", required_argument, NULL, 'L' }, + { "license", no_argument, NULL, 'l' }, + { "mft-zone-multiplier",required_argument, NULL, 'z' }, + { "no-action", no_argument, NULL, 'n' }, + { "no-indexing", no_argument, NULL, 'I' }, + { "partition-start", required_argument, NULL, 'p' }, + { "quick", no_argument, NULL, 'Q' }, + { "quiet", no_argument, NULL, 'q' }, + { "sector-size", required_argument, NULL, 's' }, + { "sectors-per-track", required_argument, NULL, 'S' }, + { "with-uuid", no_argument, NULL, 'U' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { "zero-time", no_argument, NULL, 'T' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + int lic = 0; + int help = 0; + int err = 0; + int ver = 0; + + if (!argv || !opts2) { + ntfs_log_error("Internal error: invalid parameters to " + "mkntfs_options.\n"); + return FALSE; + } + + opterr = 0; /* We'll handle the errors, thank you. */ + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A device, or a number of sectors */ + if (!opts2->dev_name) + opts2->dev_name = argv[optind - 1]; + else if (!mkntfs_parse_llong(optarg, + "number of sectors", + &opts2->num_sectors)) + err++; + break; + case 'C': + opts2->enable_compression = TRUE; + break; + case 'c': + if (!mkntfs_parse_long(optarg, "cluster size", + &opts2->cluster_size)) + err++; + break; + case 'F': + opts2->force = TRUE; + break; + case 'f': /* fast */ + case 'Q': /* quick */ + opts2->quick_format = TRUE; + break; + case 'H': + if (!mkntfs_parse_long(optarg, "heads", &opts2->heads)) + err++; + break; + case 'h': + help++; /* display help */ + break; + case 'I': + opts2->disable_indexing = TRUE; + break; + case 'L': + if (!opts2->label) { + opts2->label = argv[optind-1]; + } else { + ntfs_log_error("You may only specify the label " + "once.\n"); + err++; + } + break; + case 'l': + lic++; /* display the license */ + break; + case 'n': + opts2->no_action = TRUE; + break; + case 'p': + if (!mkntfs_parse_llong(optarg, "partition start", + &opts2->part_start_sect)) + err++; + break; + case 'q': + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET | + NTFS_LOG_LEVEL_VERBOSE | + NTFS_LOG_LEVEL_PROGRESS); + break; + case 's': + if (!mkntfs_parse_long(optarg, "sector size", + &opts2->sector_size)) + err++; + break; + case 'S': + if (!mkntfs_parse_long(optarg, "sectors per track", + &opts2->sectors_per_track)) + err++; + break; + case 'T': + opts2->use_epoch_time = TRUE; + break; + case 'U': + opts2->with_uuid = TRUE; + break; + case 'v': + ntfs_log_set_levels(NTFS_LOG_LEVEL_QUIET | + NTFS_LOG_LEVEL_VERBOSE | + NTFS_LOG_LEVEL_PROGRESS); + break; + case 'V': + ver++; /* display version info */ + break; + case 'Z': /* debug - turn on everything */ + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | + NTFS_LOG_LEVEL_TRACE | + NTFS_LOG_LEVEL_VERBOSE | + NTFS_LOG_LEVEL_QUIET); + break; + case 'z': + if (!mkntfs_parse_long(optarg, "mft zone multiplier", + &opts2->mft_zone_multiplier)) + err++; + break; + default: + if (ntfs_log_parse_option (argv[optind-1])) + break; + if (((optopt == 'c') || (optopt == 'H') || + (optopt == 'L') || (optopt == 'p') || + (optopt == 's') || (optopt == 'S') || + (optopt == 'N') || (optopt == 'z')) && + (!optarg)) { + ntfs_log_error("Option '%s' requires an " + "argument.\n", argv[optind-1]); + } else if (optopt != '?') { + ntfs_log_error("Unknown option '%s'.\n", + argv[optind - 1]); + } + err++; + break; + } + } + + if (!err && !help && !ver && !lic) { + if (opts2->dev_name == NULL) { + if (argc > 1) + ntfs_log_error("You must specify a device.\n"); + err++; + } + } + + if (ver) + mkntfs_version(); + if (lic) + mkntfs_license(); + if (err || help) + mkntfs_usage(); + + /* tri-state 0 : done, 1 : error, -1 : proceed */ + return (err ? 1 : (help || ver || lic ? 0 : -1)); +} + + +/** + * mkntfs_time + */ +static ntfs_time mkntfs_time(void) +{ + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + if (!opts.use_epoch_time) + ts.tv_sec = time(NULL); + return timespec2ntfs(ts); +} + +/** + * append_to_bad_blocks + */ +static BOOL append_to_bad_blocks(unsigned long long block) +{ + long long *new_buf; + + if (!(g_num_bad_blocks & 15)) { + new_buf = realloc(g_bad_blocks, (g_num_bad_blocks + 16) * + sizeof(long long)); + if (!new_buf) { + ntfs_log_perror("Reallocating memory for bad blocks " + "list failed"); + return FALSE; + } + g_bad_blocks = new_buf; + } + g_bad_blocks[g_num_bad_blocks++] = block; + return TRUE; +} + +/** + * mkntfs_write + */ +static long long mkntfs_write(struct ntfs_device *dev, + const void *b, long long count) +{ + long long bytes_written, total; + int retry; + + if (opts.no_action) + return count; + total = 0LL; + retry = 0; + do { + bytes_written = dev->d_ops->write(dev, b, count); + if (bytes_written == -1LL) { + retry = errno; + ntfs_log_perror("Error writing to %s", dev->d_name); + errno = retry; + return bytes_written; + } else if (!bytes_written) { + retry++; + } else { + count -= bytes_written; + total += bytes_written; + } + } while (count && retry < 3); + if (count) + ntfs_log_error("Failed to complete writing to %s after three retries." + "\n", dev->d_name); + return total; +} + +/** + * Build and write a part of the global bitmap + * without overflowing from the allocated buffer + * + * mkntfs_bitmap_write + */ +static s64 mkntfs_bitmap_write(struct ntfs_device *dev, + s64 offset, s64 length) +{ + s64 partial_length; + s64 written; + + partial_length = length; + if (partial_length > g_dynamic_buf_size) + partial_length = g_dynamic_buf_size; + /* create a partial bitmap section, and write it */ + bitmap_build(g_dynamic_buf,offset << 3,partial_length << 3); + written = dev->d_ops->write(dev, g_dynamic_buf, partial_length); + return (written); +} + +/** + * Build and write a part of the log file + * without overflowing from the allocated buffer + * + * mkntfs_logfile_write + */ +static s64 mkntfs_logfile_write(struct ntfs_device *dev, + s64 offset __attribute__((unused)), s64 length) +{ + s64 partial_length; + s64 written; + + partial_length = length; + if (partial_length > g_dynamic_buf_size) + partial_length = g_dynamic_buf_size; + /* create a partial bad cluster section, and write it */ + memset(g_dynamic_buf, -1, partial_length); + written = dev->d_ops->write(dev, g_dynamic_buf, partial_length); + return (written); +} + +/** + * ntfs_rlwrite - Write to disk the clusters contained in the runlist @rl + * taking the data from @val. Take @val_len bytes from @val and pad the + * rest with zeroes. + * + * If the @rl specifies a completely sparse file, @val is allowed to be NULL. + * + * @inited_size if not NULL points to an output variable which will contain + * the actual number of bytes written to disk. I.e. this will not include + * sparse bytes for example. + * + * Return the number of bytes written (minus padding) or -1 on error. Errno + * will be set to the error code. + */ +static s64 ntfs_rlwrite(struct ntfs_device *dev, const runlist *rl, + const u8 *val, const s64 val_len, s64 *inited_size, + WRITE_TYPE write_type) +{ + s64 bytes_written, total, length, delta; + int retry, i; + + if (inited_size) + *inited_size = 0LL; + if (opts.no_action) + return val_len; + total = 0LL; + delta = 0LL; + for (i = 0; rl[i].length; i++) { + length = rl[i].length * g_vol->cluster_size; + /* Don't write sparse runs. */ + if (rl[i].lcn == -1) { + total += length; + if (!val) + continue; + /* TODO: Check that *val is really zero at pos and len. */ + continue; + } + /* + * Break up the write into the real data write and then a write + * of zeroes between the end of the real data and the end of + * the (last) run. + */ + if (total + length > val_len) { + delta = length; + length = val_len - total; + delta -= length; + } + if (dev->d_ops->seek(dev, rl[i].lcn * g_vol->cluster_size, + SEEK_SET) == (off_t)-1) + return -1LL; + retry = 0; + do { + /* use specific functions if buffer is not prefilled */ + switch (write_type) { + case WRITE_BITMAP : + bytes_written = mkntfs_bitmap_write(dev, + total, length); + break; + case WRITE_LOGFILE : + bytes_written = mkntfs_logfile_write(dev, + total, length); + break; + default : + bytes_written = dev->d_ops->write(dev, + val + total, length); + break; + } + if (bytes_written == -1LL) { + retry = errno; + ntfs_log_perror("Error writing to %s", + dev->d_name); + errno = retry; + return bytes_written; + } + if (bytes_written) { + length -= bytes_written; + total += bytes_written; + if (inited_size) + *inited_size += bytes_written; + } else { + retry++; + } + } while (length && retry < 3); + if (length) { + ntfs_log_error("Failed to complete writing to %s after three " + "retries.\n", dev->d_name); + return total; + } + } + if (delta) { + int eo; + char *b = ntfs_calloc(delta); + if (!b) + return -1; + bytes_written = mkntfs_write(dev, b, delta); + eo = errno; + free(b); + errno = eo; + if (bytes_written == -1LL) + return bytes_written; + } + return total; +} + +/** + * make_room_for_attribute - make room for an attribute inside an mft record + * @m: mft record + * @pos: position at which to make space + * @size: byte size to make available at this position + * + * @pos points to the attribute in front of which we want to make space. + * + * Return 0 on success or -errno on error. Possible error codes are: + * + * -ENOSPC There is not enough space available to complete + * operation. The caller has to make space before calling + * this. + * -EINVAL Can only occur if mkntfs was compiled with -DDEBUG. Means + * the input parameters were faulty. + */ +static int make_room_for_attribute(MFT_RECORD *m, char *pos, const u32 size) +{ + u32 biu; + + if (!size) + return 0; +#ifdef DEBUG + /* + * Rigorous consistency checks. Always return -EINVAL even if more + * appropriate codes exist for simplicity of parsing the return value. + */ + if (size != ((size + 7) & ~7)) { + ntfs_log_error("make_room_for_attribute() received non 8-byte aligned " + "size.\n"); + return -EINVAL; + } + if (!m || !pos) + return -EINVAL; + if (pos < (char*)m || pos + size < (char*)m || + pos > (char*)m + le32_to_cpu(m->bytes_allocated) || + pos + size > (char*)m + le32_to_cpu(m->bytes_allocated)) + return -EINVAL; + /* The -8 is for the attribute terminator. */ + if (pos - (char*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) + return -EINVAL; +#endif + biu = le32_to_cpu(m->bytes_in_use); + /* Do we have enough space? */ + if (biu + size > le32_to_cpu(m->bytes_allocated)) + return -ENOSPC; + /* Move everything after pos to pos + size. */ + memmove(pos + size, pos, biu - (pos - (char*)m)); + /* Update mft record. */ + m->bytes_in_use = cpu_to_le32(biu + size); + return 0; +} + +/** + * deallocate_scattered_clusters + */ +static void deallocate_scattered_clusters(const runlist *rl) +{ + int i; + + if (!rl) + return; + /* Iterate over all runs in the runlist @rl. */ + for (i = 0; rl[i].length; i++) { + /* Skip sparse runs. */ + if (rl[i].lcn == -1LL) + continue; + /* Deallocate the current run. */ + bitmap_deallocate(rl[i].lcn, rl[i].length); + } +} + +/** + * allocate_scattered_clusters + * @clusters: Amount of clusters to allocate. + * + * Allocate @clusters and create a runlist of the allocated clusters. + * + * Return the allocated runlist. Caller has to free the runlist when finished + * with it. + * + * On error return NULL and errno is set to the error code. + * + * TODO: We should be returning the size as well, but for mkntfs this is not + * necessary. + */ +static runlist * allocate_scattered_clusters(s64 clusters) +{ + runlist *rl = NULL, *rlt; + VCN vcn = 0LL; + LCN lcn, end, prev_lcn = 0LL; + int rlpos = 0; + int rlsize = 0; + s64 prev_run_len = 0LL; + char bit; + + end = g_vol->nr_clusters; + /* Loop until all clusters are allocated. */ + while (clusters) { + /* Loop in current zone until we run out of free clusters. */ + for (lcn = g_mft_zone_end; lcn < end; lcn++) { + bit = bitmap_get_and_set(lcn,1); + if (bit) + continue; + /* + * Reallocate memory if necessary. Make sure we have + * enough for the terminator entry as well. + */ + if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { + rlsize += 4096; /* PAGE_SIZE */ + rlt = realloc(rl, rlsize); + if (!rlt) + goto err_end; + rl = rlt; + } + /* Coalesce with previous run if adjacent LCNs. */ + if (prev_lcn == lcn - prev_run_len) { + rl[rlpos - 1].length = ++prev_run_len; + vcn++; + } else { + rl[rlpos].vcn = vcn++; + rl[rlpos].lcn = lcn; + prev_lcn = lcn; + rl[rlpos].length = 1LL; + prev_run_len = 1LL; + rlpos++; + } + /* Done? */ + if (!--clusters) { + /* Add terminator element and return. */ + rl[rlpos].vcn = vcn; + rl[rlpos].lcn = 0LL; + rl[rlpos].length = 0LL; + return rl; + } + + } + /* Switch to next zone, decreasing mft zone by factor 2. */ + end = g_mft_zone_end; + g_mft_zone_end >>= 1; + /* Have we run out of space on the volume? */ + if (g_mft_zone_end <= 0) + goto err_end; + } + return rl; +err_end: + if (rl) { + /* Add terminator element. */ + rl[rlpos].vcn = vcn; + rl[rlpos].lcn = -1LL; + rl[rlpos].length = 0LL; + /* Deallocate all allocated clusters. */ + deallocate_scattered_clusters(rl); + /* Free the runlist. */ + free(rl); + } + return NULL; +} + +/** + * ntfs_attr_find - find (next) attribute in mft record + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * You shouldn't need to call this function directly. Use lookup_attr() instead. + * + * ntfs_attr_find() takes a search context @ctx as parameter and searches the + * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an + * attribute of @type, optionally @name and @val. If found, ntfs_attr_find() + * returns 0 and @ctx->attr will point to the found attribute. + * + * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and + * @ctx->attr will point to the attribute before which the attribute being + * searched for would need to be inserted if such an action were to be desired. + * + * On actual error, ntfs_attr_find() returns -1 with errno set to the error + * code but not to ENOENT. In this case @ctx->attr is undefined and in + * particular do not rely on it not changing. + * + * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it + * is FALSE, the search begins after @ctx->attr. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to + * indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_find() will return the next attribute in the + * mft record @ctx->mrec. + * + * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT. + * AT_END is not a valid attribute, its length is zero for example, thus it is + * safer to return error instead of success in this case. This also allows us + * to interoperate cleanly with ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and + * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record + * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at + * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case + * sensitive. When @name is present, @name_len is the @name length in Unicode + * characters. + * + * If @name is not present (NULL), we assume that the unnamed attribute is + * being searched for. + * + * Finally, the resident attribute value @val is looked for, if present. + * If @val is not present (NULL), @val_len is ignored. + * + * ntfs_attr_find() only searches the specified mft record and it ignores the + * presence of an attribute list attribute (unless it is the one being searched + * for, obviously). If you need to take attribute lists into consideration, use + * ntfs_attr_lookup() instead (see below). This also means that you cannot use + * ntfs_attr_find() to search for extent records of non-resident attributes, as + * extents with lowest_vcn != 0 are usually described by the attribute list + * attribute only. - Note that it is possible that the first extent is only in + * the attribute list while the last extent is in the base mft record, so don't + * rely on being able to find the first extent in the base mft record. + * + * Warning: Never use @val when looking for attribute types which can be + * non-resident as this most likely will result in a crash! + */ +static int mkntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) +{ + ATTR_RECORD *a; + ntfschar *upcase = g_vol->upcase; + u32 upcase_len = g_vol->upcase_len; + + /* + * Iterate over attributes in mft record starting at @ctx->attr, or the + * attribute following that, if @ctx->is_first is TRUE. + */ + if (ctx->is_first) { + a = ctx->attr; + ctx->is_first = FALSE; + } else { + a = (ATTR_RECORD*)((char*)ctx->attr + + le32_to_cpu(ctx->attr->length)); + } + for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { + if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec + + le32_to_cpu(ctx->mrec->bytes_allocated)) + break; + ctx->attr = a; + if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > + le32_to_cpu(type))) || + (a->type == AT_END)) { + errno = ENOENT; + return -1; + } + if (!a->length) + break; + /* If this is an enumeration return this attribute. */ + if (type == AT_UNUSED) + return 0; + if (a->type != type) + continue; + /* + * If @name is AT_UNNAMED we want an unnamed attribute. + * If @name is present, compare the two names. + * Otherwise, match any attribute. + */ + if (name == AT_UNNAMED) { + /* The search failed if the found attribute is named. */ + if (a->name_length) { + errno = ENOENT; + return -1; + } + } else if (name && !ntfs_names_are_equal(name, name_len, + (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)), + a->name_length, ic, upcase, upcase_len)) { + int rc; + + rc = ntfs_names_full_collate(name, name_len, + (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, IGNORE_CASE, + upcase, upcase_len); + /* + * If @name collates before a->name, there is no + * matching attribute. + */ + if (rc == -1) { + errno = ENOENT; + return -1; + } + /* If the strings are not equal, continue search. */ + if (rc) + continue; + rc = ntfs_names_full_collate(name, name_len, + (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, CASE_SENSITIVE, + upcase, upcase_len); + if (rc == -1) { + errno = ENOENT; + return -1; + } + if (rc) + continue; + } + /* + * The names match or @name not present and attribute is + * unnamed. If no @val specified, we have found the attribute + * and are done. + */ + if (!val) { + return 0; + /* @val is present; compare values. */ + } else { + int rc; + + rc = memcmp(val, (char*)a +le16_to_cpu(a->value_offset), + min(val_len, + le32_to_cpu(a->value_length))); + /* + * If @val collates before the current attribute's + * value, there is no matching attribute. + */ + if (!rc) { + u32 avl; + avl = le32_to_cpu(a->value_length); + if (val_len == avl) + return 0; + if (val_len < avl) { + errno = ENOENT; + return -1; + } + } else if (rc < 0) { + errno = ENOENT; + return -1; + } + } + } + ntfs_log_trace("File is corrupt. Run chkdsk.\n"); + errno = EIO; + return -1; +} + +/** + * ntfs_attr_lookup - find an attribute in an ntfs inode + * @type: attribute type to find + * @name: attribute name to find (optional, i.e. NULL means don't care) + * @name_len: attribute name length (only needed if @name present) + * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) + * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) + * @val: attribute value to find (optional, resident attributes only) + * @val_len: attribute value length + * @ctx: search context with mft record and attribute to search from + * + * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must + * be the base mft record and @ctx must have been obtained from a call to + * ntfs_attr_get_search_ctx(). + * + * This function transparently handles attribute lists and @ctx is used to + * continue searches where they were left off at. + * + * If @type is AT_UNUSED, return the first found attribute, i.e. one can + * enumerate all attributes by setting @type to AT_UNUSED and then calling + * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT + * to indicate that there are no more entries. During the enumeration, each + * successful call of ntfs_attr_lookup() will return the next attribute, with + * the current attribute being described by the search context @ctx. + * + * If @type is AT_END, seek to the end of the base mft record ignoring the + * attribute list completely and return -1 with errno set to ENOENT. AT_END is + * not a valid attribute, its length is zero for example, thus it is safer to + * return error instead of success in this case. It should never be needed to + * do this, but we implement the functionality because it allows for simpler + * code inside ntfs_external_attr_find(). + * + * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present + * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, + * match both named and unnamed attributes. + * + * After finishing with the attribute/mft record you need to call + * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any + * mapped extent inodes, etc). + * + * Return 0 if the search was successful and -1 if not, with errno set to the + * error code. + * + * On success, @ctx->attr is the found attribute, it is in mft record + * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this + * attribute with @ctx->base_* being the base mft record to which @ctx->attr + * belongs. If no attribute list attribute is present @ctx->al_entry and + * @ctx->base_* are NULL. + * + * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the + * attribute which collates just after the attribute being searched for in the + * base ntfs inode, i.e. if one wants to add the attribute to the mft record + * this is the correct place to insert it into, and if there is not enough + * space, the attribute should be placed in an extent mft record. + * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list + * at which the new attribute's attribute list entry should be inserted. The + * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. + * The only exception to this is when @type is AT_END, in which case + * @ctx->al_entry is set to NULL also (see above). + * + * The following error codes are defined: + * ENOENT Attribute not found, not an error as such. + * EINVAL Invalid arguments. + * EIO I/O error or corrupt data structures found. + * ENOMEM Not enough memory to allocate necessary buffers. + */ +static int mkntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const VCN lowest_vcn __attribute__((unused)), const u8 *val, + const u32 val_len, ntfs_attr_search_ctx *ctx) +{ + ntfs_inode *base_ni; + + if (!ctx || !ctx->mrec || !ctx->attr) { + errno = EINVAL; + return -1; + } + if (ctx->base_ntfs_ino) + base_ni = ctx->base_ntfs_ino; + else + base_ni = ctx->ntfs_ino; + if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) + return mkntfs_attr_find(type, name, name_len, ic, val, val_len, + ctx); + errno = EOPNOTSUPP; + return -1; +} + +/** + * insert_positioned_attr_in_mft_record + * + * Create a non-resident attribute with a predefined on disk location + * specified by the runlist @rl. The clusters specified by @rl are assumed to + * be allocated already. + * + * Return 0 on success and -errno on error. + */ +static int insert_positioned_attr_in_mft_record(MFT_RECORD *m, + const ATTR_TYPES type, const char *name, u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const runlist *rl, const u8 *val, const s64 val_len) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + u16 hdr_size; + int asize, mpa_size, err, i; + s64 bw = 0, inited_size; + VCN highest_vcn; + ntfschar *uname = NULL; + int uname_len = 0; + /* + if (base record) + attr_lookup(); + else + */ + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + err = -ENOMEM; + goto err_out; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #1.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, NULL, 0, ctx)) { + err = -EEXIST; + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_error("Corrupt inode.\n"); + err = -errno; + goto err_out; + } + a = ctx->attr; + if (flags & ATTR_COMPRESSION_MASK) { + ntfs_log_error("Compressed attributes not supported yet.\n"); + /* FIXME: Compress attribute into a temporary buffer, set */ + /* val accordingly and save the compressed size. */ + err = -EOPNOTSUPP; + goto err_out; + } + if (flags & (ATTR_IS_ENCRYPTED | ATTR_IS_SPARSE)) { + ntfs_log_error("Encrypted/sparse attributes not supported.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (flags & ATTR_COMPRESSION_MASK) { + hdr_size = 72; + /* FIXME: This compression stuff is all wrong. Never mind for */ + /* now. (AIA) */ + if (val_len) + mpa_size = 0; /* get_size_for_compressed_mapping_pairs(rl); */ + else + mpa_size = 0; + } else { + hdr_size = 64; + if (val_len) { + mpa_size = ntfs_get_size_for_mapping_pairs(g_vol, rl, 0, INT_MAX); + if (mpa_size < 0) { + err = -errno; + ntfs_log_error("Failed to get size for mapping " + "pairs.\n"); + goto err_out; + } + } else { + mpa_size = 0; + } + } + /* Mapping pairs array and next attribute must be 8-byte aligned. */ + asize = (((int)hdr_size + ((name_len + 7) & ~7) + mpa_size) + 7) & ~7; + /* Get the highest vcn. */ + for (i = 0, highest_vcn = 0LL; rl[i].length; i++) + highest_vcn += rl[i].length; + /* Does the value fit inside the allocated size? */ + if (highest_vcn * g_vol->cluster_size < val_len) { + ntfs_log_error("BUG: Allocated size is smaller than data size!\n"); + err = -EINVAL; + goto err_out; + } + err = make_room_for_attribute(m, (char*)a, asize); + if (err == -ENOSPC) { + /* + * FIXME: Make space! (AIA) + * can we make it non-resident? if yes, do that. + * does it fit now? yes -> do it. + * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? + * yes -> make non-resident + * does it fit now? yes -> do it. + * make all attributes non-resident + * does it fit now? yes -> do it. + * m is a base record? yes -> allocate extension record + * does the new attribute fit in there? yes -> do it. + * split up runlist into extents and place each in an extension + * record. + * FIXME: the check for needing extension records should be + * earlier on as it is very quick: asize > m->bytes_allocated? + */ + err = -EOPNOTSUPP; + goto err_out; +#ifdef DEBUG + } else if (err == -EINVAL) { + ntfs_log_error("BUG(): in insert_positioned_attribute_in_mft_" + "record(): make_room_for_attribute() returned " + "error: EINVAL!\n"); + goto err_out; +#endif + } + a->type = type; + a->length = cpu_to_le32(asize); + a->non_resident = 1; + a->name_length = name_len; + a->name_offset = cpu_to_le16(hdr_size); + a->flags = flags; + a->instance = m->next_attr_instance; + m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) + + 1) & 0xffff); + a->lowest_vcn = cpu_to_le64(0); + a->highest_vcn = cpu_to_sle64(highest_vcn - 1LL); + a->mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); + memset(a->reserved1, 0, sizeof(a->reserved1)); + /* FIXME: Allocated size depends on compression. */ + a->allocated_size = cpu_to_sle64(highest_vcn * g_vol->cluster_size); + a->data_size = cpu_to_sle64(val_len); + if (name_len) + memcpy((char*)a + hdr_size, uname, name_len << 1); + if (flags & ATTR_COMPRESSION_MASK) { + if (flags & ATTR_COMPRESSION_MASK & ~ATTR_IS_COMPRESSED) { + ntfs_log_error("Unknown compression format. Reverting " + "to standard compression.\n"); + a->flags &= ~ATTR_COMPRESSION_MASK; + a->flags |= ATTR_IS_COMPRESSED; + } + a->compression_unit = 4; + inited_size = val_len; + /* FIXME: Set the compressed size. */ + a->compressed_size = cpu_to_le64(0); + /* FIXME: Write out the compressed data. */ + /* FIXME: err = build_mapping_pairs_compressed(); */ + err = -EOPNOTSUPP; + } else { + a->compression_unit = 0; + if ((type == AT_DATA) + && (m->mft_record_number + == const_cpu_to_le32(FILE_LogFile))) + bw = ntfs_rlwrite(g_vol->dev, rl, val, val_len, + &inited_size, WRITE_LOGFILE); + else + bw = ntfs_rlwrite(g_vol->dev, rl, val, val_len, + &inited_size, WRITE_STANDARD); + if (bw != val_len) { + ntfs_log_error("Error writing non-resident attribute " + "value.\n"); + return -errno; + } + err = ntfs_mapping_pairs_build(g_vol, (u8*)a + hdr_size + + ((name_len + 7) & ~7), mpa_size, rl, 0, NULL); + } + a->initialized_size = cpu_to_sle64(inited_size); + if (err < 0 || bw != val_len) { + /* FIXME: Handle error. */ + /* deallocate clusters */ + /* remove attribute */ + if (err >= 0) + err = -EIO; + ntfs_log_error("insert_positioned_attr_in_mft_record failed " + "with error %i.\n", err < 0 ? err : (int)bw); + } +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + ntfs_ucsfree(uname); + return err; +} + +/** + * insert_non_resident_attr_in_mft_record + * + * Return 0 on success and -errno on error. + */ +static int insert_non_resident_attr_in_mft_record(MFT_RECORD *m, + const ATTR_TYPES type, const char *name, u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const u8 *val, const s64 val_len, + WRITE_TYPE write_type) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + u16 hdr_size; + int asize, mpa_size, err, i; + runlist *rl = NULL; + s64 bw = 0; + ntfschar *uname = NULL; + int uname_len = 0; + /* + if (base record) + attr_lookup(); + else + */ + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + err = -ENOMEM; + goto err_out; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #2.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, NULL, 0, ctx)) { + err = -EEXIST; + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_error("Corrupt inode.\n"); + err = -errno; + goto err_out; + } + a = ctx->attr; + if (flags & ATTR_COMPRESSION_MASK) { + ntfs_log_error("Compressed attributes not supported yet.\n"); + /* FIXME: Compress attribute into a temporary buffer, set */ + /* val accordingly and save the compressed size. */ + err = -EOPNOTSUPP; + goto err_out; + } + if (flags & (ATTR_IS_ENCRYPTED | ATTR_IS_SPARSE)) { + ntfs_log_error("Encrypted/sparse attributes not supported.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (val_len) { + rl = allocate_scattered_clusters((val_len + + g_vol->cluster_size - 1) / g_vol->cluster_size); + if (!rl) { + err = -errno; + ntfs_log_perror("Failed to allocate scattered clusters"); + goto err_out; + } + } else { + rl = NULL; + } + if (flags & ATTR_COMPRESSION_MASK) { + hdr_size = 72; + /* FIXME: This compression stuff is all wrong. Never mind for */ + /* now. (AIA) */ + if (val_len) + mpa_size = 0; /* get_size_for_compressed_mapping_pairs(rl); */ + else + mpa_size = 0; + } else { + hdr_size = 64; + if (val_len) { + mpa_size = ntfs_get_size_for_mapping_pairs(g_vol, rl, 0, INT_MAX); + if (mpa_size < 0) { + err = -errno; + ntfs_log_error("Failed to get size for mapping " + "pairs.\n"); + goto err_out; + } + } else { + mpa_size = 0; + } + } + /* Mapping pairs array and next attribute must be 8-byte aligned. */ + asize = (((int)hdr_size + ((name_len + 7) & ~7) + mpa_size) + 7) & ~7; + err = make_room_for_attribute(m, (char*)a, asize); + if (err == -ENOSPC) { + /* + * FIXME: Make space! (AIA) + * can we make it non-resident? if yes, do that. + * does it fit now? yes -> do it. + * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? + * yes -> make non-resident + * does it fit now? yes -> do it. + * make all attributes non-resident + * does it fit now? yes -> do it. + * m is a base record? yes -> allocate extension record + * does the new attribute fit in there? yes -> do it. + * split up runlist into extents and place each in an extension + * record. + * FIXME: the check for needing extension records should be + * earlier on as it is very quick: asize > m->bytes_allocated? + */ + err = -EOPNOTSUPP; + goto err_out; +#ifdef DEBUG + } else if (err == -EINVAL) { + ntfs_log_error("BUG(): in insert_non_resident_attribute_in_" + "mft_record(): make_room_for_attribute() " + "returned error: EINVAL!\n"); + goto err_out; +#endif + } + a->type = type; + a->length = cpu_to_le32(asize); + a->non_resident = 1; + a->name_length = name_len; + a->name_offset = cpu_to_le16(hdr_size); + a->flags = flags; + a->instance = m->next_attr_instance; + m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) + + 1) & 0xffff); + a->lowest_vcn = cpu_to_le64(0); + for (i = 0; rl[i].length; i++) + ; + a->highest_vcn = cpu_to_sle64(rl[i].vcn - 1); + a->mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); + memset(a->reserved1, 0, sizeof(a->reserved1)); + /* FIXME: Allocated size depends on compression. */ + a->allocated_size = cpu_to_sle64((val_len + (g_vol->cluster_size - 1)) & + ~(g_vol->cluster_size - 1)); + a->data_size = cpu_to_sle64(val_len); + a->initialized_size = cpu_to_sle64(val_len); + if (name_len) + memcpy((char*)a + hdr_size, uname, name_len << 1); + if (flags & ATTR_COMPRESSION_MASK) { + if (flags & ATTR_COMPRESSION_MASK & ~ATTR_IS_COMPRESSED) { + ntfs_log_error("Unknown compression format. Reverting " + "to standard compression.\n"); + a->flags &= ~ATTR_COMPRESSION_MASK; + a->flags |= ATTR_IS_COMPRESSED; + } + a->compression_unit = 4; + /* FIXME: Set the compressed size. */ + a->compressed_size = cpu_to_le64(0); + /* FIXME: Write out the compressed data. */ + /* FIXME: err = build_mapping_pairs_compressed(); */ + err = -EOPNOTSUPP; + } else { + a->compression_unit = 0; + bw = ntfs_rlwrite(g_vol->dev, rl, val, val_len, NULL, + write_type); + if (bw != val_len) { + ntfs_log_error("Error writing non-resident attribute " + "value.\n"); + return -errno; + } + err = ntfs_mapping_pairs_build(g_vol, (u8*)a + hdr_size + + ((name_len + 7) & ~7), mpa_size, rl, 0, NULL); + } + if (err < 0 || bw != val_len) { + /* FIXME: Handle error. */ + /* deallocate clusters */ + /* remove attribute */ + if (err >= 0) + err = -EIO; + ntfs_log_error("insert_non_resident_attr_in_mft_record failed with " + "error %lld.\n", (long long) (err < 0 ? err : bw)); + } +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + ntfs_ucsfree(uname); + free(rl); + return err; +} + +/** + * insert_resident_attr_in_mft_record + * + * Return 0 on success and -errno on error. + */ +static int insert_resident_attr_in_mft_record(MFT_RECORD *m, + const ATTR_TYPES type, const char *name, u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const RESIDENT_ATTR_FLAGS res_flags, + const u8 *val, const u32 val_len) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + int asize, err; + ntfschar *uname = NULL; + int uname_len = 0; + /* + if (base record) + mkntfs_attr_lookup(); + else + */ + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + err = -ENOMEM; + goto err_out; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #3.\n"); + err = -EOPNOTSUPP; + goto err_out; + } + if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, val, val_len, + ctx)) { + err = -EEXIST; + goto err_out; + } + if (errno != ENOENT) { + ntfs_log_error("Corrupt inode.\n"); + err = -errno; + goto err_out; + } + a = ctx->attr; + /* sizeof(resident attribute record header) == 24 */ + asize = ((24 + ((name_len*2 + 7) & ~7) + val_len) + 7) & ~7; + err = make_room_for_attribute(m, (char*)a, asize); + if (err == -ENOSPC) { + /* + * FIXME: Make space! (AIA) + * can we make it non-resident? if yes, do that. + * does it fit now? yes -> do it. + * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? + * yes -> make non-resident + * does it fit now? yes -> do it. + * make all attributes non-resident + * does it fit now? yes -> do it. + * m is a base record? yes -> allocate extension record + * does the new attribute fit in there? yes -> do it. + * split up runlist into extents and place each in an extension + * record. + * FIXME: the check for needing extension records should be + * earlier on as it is very quick: asize > m->bytes_allocated? + */ + err = -EOPNOTSUPP; + goto err_out; + } +#ifdef DEBUG + if (err == -EINVAL) { + ntfs_log_error("BUG(): in insert_resident_attribute_in_mft_" + "record(): make_room_for_attribute() returned " + "error: EINVAL!\n"); + goto err_out; + } +#endif + a->type = type; + a->length = cpu_to_le32(asize); + a->non_resident = 0; + a->name_length = name_len; + if (type == AT_OBJECT_ID) + a->name_offset = const_cpu_to_le16(0); + else + a->name_offset = const_cpu_to_le16(24); + a->flags = flags; + a->instance = m->next_attr_instance; + m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) + + 1) & 0xffff); + a->value_length = cpu_to_le32(val_len); + a->value_offset = cpu_to_le16(24 + ((name_len*2 + 7) & ~7)); + a->resident_flags = res_flags; + a->reservedR = 0; + if (name_len) + memcpy((char*)a + 24, uname, name_len << 1); + if (val_len) + memcpy((char*)a + le16_to_cpu(a->value_offset), val, val_len); +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + ntfs_ucsfree(uname); + return err; +} + + +/** + * add_attr_std_info + * + * Return 0 on success or -errno on error. + */ +static int add_attr_std_info(MFT_RECORD *m, const FILE_ATTR_FLAGS flags, + le32 security_id) +{ + STANDARD_INFORMATION si; + int err, sd_size; + + sd_size = 48; + + si.creation_time = mkntfs_time(); + si.last_data_change_time = si.creation_time; + si.last_mft_change_time = si.creation_time; + si.last_access_time = si.creation_time; + si.file_attributes = flags; /* already LE */ + si.maximum_versions = cpu_to_le32(0); + si.version_number = cpu_to_le32(0); + si.class_id = cpu_to_le32(0); + si.security_id = security_id; + if (si.security_id != const_cpu_to_le32(0)) + sd_size = 72; + /* FIXME: $Quota support... */ + si.owner_id = cpu_to_le32(0); + si.quota_charged = cpu_to_le64(0ULL); + /* FIXME: $UsnJrnl support... Not needed on fresh w2k3-volume */ + si.usn = cpu_to_le64(0ULL); + /* NTFS 1.2: size of si = 48, NTFS 3.[01]: size of si = 72 */ + err = insert_resident_attr_in_mft_record(m, AT_STANDARD_INFORMATION, + NULL, 0, CASE_SENSITIVE, const_cpu_to_le16(0), + 0, (u8*)&si, sd_size); + if (err < 0) + ntfs_log_perror("add_attr_std_info failed"); + return err; +} + +/* + * Tell whether the unnamed data is non resident + */ + +static BOOL non_resident_unnamed_data(MFT_RECORD *m) +{ + ATTR_RECORD *a; + ntfs_attr_search_ctx *ctx; + BOOL nonres; + + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (ctx && !mkntfs_attr_find(AT_DATA, + (const ntfschar*)NULL, 0, CASE_SENSITIVE, + (u8*)NULL, 0, ctx)) { + a = ctx->attr; + nonres = a->non_resident != 0; + } else { + ntfs_log_error("BUG: Unnamed data not found\n"); + nonres = TRUE; + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return (nonres); +} + +/* + * Get the time stored in the standard information attribute + */ + +static ntfs_time stdinfo_time(MFT_RECORD *m) +{ + STANDARD_INFORMATION *si; + ntfs_attr_search_ctx *ctx; + ntfs_time info_time; + + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (ctx && !mkntfs_attr_find(AT_STANDARD_INFORMATION, + (const ntfschar*)NULL, 0, CASE_SENSITIVE, + (u8*)NULL, 0, ctx)) { + si = (STANDARD_INFORMATION*)((char*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + info_time = si->creation_time; + } else { + ntfs_log_error("BUG: Standard information not found\n"); + info_time = mkntfs_time(); + } + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return (info_time); +} + +/** + * add_attr_file_name + * + * Return 0 on success or -errno on error. + */ +static int add_attr_file_name(MFT_RECORD *m, const leMFT_REF parent_dir, + const s64 allocated_size, const s64 data_size, + const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, + const u32 reparse_point_tag, const char *file_name, + const FILE_NAME_TYPE_FLAGS file_name_type) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *si; + FILE_NAME_ATTR *fn; + int i, fn_size; + ntfschar *uname; + + /* Check if the attribute is already there. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to get attribute search context.\n"); + return -ENOMEM; + } + if (mkntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + int eo = errno; + ntfs_log_error("BUG: Standard information attribute not " + "present in file record.\n"); + ntfs_attr_put_search_ctx(ctx); + return -eo; + } + si = (STANDARD_INFORMATION*)((char*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + i = (strlen(file_name) + 1) * sizeof(ntfschar); + fn_size = sizeof(FILE_NAME_ATTR) + i; + fn = ntfs_malloc(fn_size); + if (!fn) { + ntfs_attr_put_search_ctx(ctx); + return -errno; + } + fn->parent_directory = parent_dir; + + fn->creation_time = si->creation_time; + fn->last_data_change_time = si->last_data_change_time; + fn->last_mft_change_time = si->last_mft_change_time; + fn->last_access_time = si->last_access_time; + ntfs_attr_put_search_ctx(ctx); + + fn->allocated_size = cpu_to_sle64(allocated_size); + fn->data_size = cpu_to_sle64(data_size); + fn->file_attributes = flags; + /* These are in a union so can't have both. */ + if (packed_ea_size && reparse_point_tag) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + fn->packed_ea_size = cpu_to_le16(packed_ea_size); + fn->reserved = cpu_to_le16(0); + } else { + fn->reparse_point_tag = cpu_to_le32(reparse_point_tag); + } + fn->file_name_type = file_name_type; + uname = fn->file_name; + i = ntfs_mbstoucs_libntfscompat(file_name, &uname, i); + if (i < 1) { + free(fn); + return -EINVAL; + } + if (i > 0xff) { + free(fn); + return -ENAMETOOLONG; + } + /* No terminating null in file names. */ + fn->file_name_length = i; + fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); + i = insert_resident_attr_in_mft_record(m, AT_FILE_NAME, NULL, 0, + CASE_SENSITIVE, const_cpu_to_le16(0), + RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); + free(fn); + if (i < 0) + ntfs_log_error("add_attr_file_name failed: %s\n", strerror(-i)); + return i; +} + +/** + * add_attr_object_id - + * + * Note we insert only a basic object id which only has the GUID and none of + * the extended fields. This is because we currently only use this function + * when creating the object id for the volume. + * + * Return 0 on success or -errno on error. + */ +static int add_attr_object_id(MFT_RECORD *m, const GUID *object_id) +{ + OBJECT_ID_ATTR oi; + int err; + + oi = (OBJECT_ID_ATTR) { + .object_id = *object_id, + }; + err = insert_resident_attr_in_mft_record(m, AT_OBJECT_ID, NULL, + 0, CASE_SENSITIVE, const_cpu_to_le16(0), + 0, (u8*)&oi, sizeof(oi.object_id)); + if (err < 0) + ntfs_log_error("add_attr_vol_info failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_sd + * + * Create the security descriptor attribute adding the security descriptor @sd + * of length @sd_len to the mft record @m. + * + * Return 0 on success or -errno on error. + */ +static int add_attr_sd(MFT_RECORD *m, const u8 *sd, const s64 sd_len) +{ + int err; + + /* Does it fit? NO: create non-resident. YES: create resident. */ + if (le32_to_cpu(m->bytes_in_use) + 24 + sd_len > + le32_to_cpu(m->bytes_allocated)) + err = insert_non_resident_attr_in_mft_record(m, + AT_SECURITY_DESCRIPTOR, NULL, 0, + CASE_SENSITIVE, const_cpu_to_le16(0), sd, + sd_len, WRITE_STANDARD); + else + err = insert_resident_attr_in_mft_record(m, + AT_SECURITY_DESCRIPTOR, NULL, 0, + CASE_SENSITIVE, const_cpu_to_le16(0), 0, sd, + sd_len); + if (err < 0) + ntfs_log_error("add_attr_sd failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_data + * + * Return 0 on success or -errno on error. + */ +static int add_attr_data(MFT_RECORD *m, const char *name, const u32 name_len, + const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, + const u8 *val, const s64 val_len) +{ + int err; + + /* + * Does it fit? NO: create non-resident. YES: create resident. + * + * FIXME: Introduced arbitrary limit of mft record allocated size - 512. + * This is to get around the problem that if $Bitmap/$DATA becomes too + * big, but is just small enough to be resident, we would make it + * resident, and later run out of space when creating the other + * attributes and this would cause us to abort as making resident + * attributes non-resident is not supported yet. + * The proper fix is to support making resident attribute non-resident. + */ + if (le32_to_cpu(m->bytes_in_use) + 24 + val_len > + min(le32_to_cpu(m->bytes_allocated), + le32_to_cpu(m->bytes_allocated) - 512)) + err = insert_non_resident_attr_in_mft_record(m, AT_DATA, name, + name_len, ic, flags, val, val_len, + WRITE_STANDARD); + else + err = insert_resident_attr_in_mft_record(m, AT_DATA, name, + name_len, ic, flags, 0, val, val_len); + + if (err < 0) + ntfs_log_error("add_attr_data failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_data_positioned + * + * Create a non-resident data attribute with a predefined on disk location + * specified by the runlist @rl. The clusters specified by @rl are assumed to + * be allocated already. + * + * Return 0 on success or -errno on error. + */ +static int add_attr_data_positioned(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const ATTR_FLAGS flags, const runlist *rl, + const u8 *val, const s64 val_len) +{ + int err; + + err = insert_positioned_attr_in_mft_record(m, AT_DATA, name, name_len, + ic, flags, rl, val, val_len); + if (err < 0) + ntfs_log_error("add_attr_data_positioned failed: %s\n", + strerror(-err)); + return err; +} + +/** + * add_attr_vol_name + * + * Create volume name attribute specifying the volume name @vol_name as a null + * terminated char string of length @vol_name_len (number of characters not + * including the terminating null), which is converted internally to a little + * endian ntfschar string. The name is at least 1 character long (though + * Windows accepts zero characters), and at most 128 characters long (not + * counting the terminating null). + * + * Return 0 on success or -errno on error. + */ +static int add_attr_vol_name(MFT_RECORD *m, const char *vol_name, + const int vol_name_len __attribute__((unused))) +{ + ntfschar *uname = NULL; + int uname_len = 0; + int i; + + if (vol_name) { + uname_len = ntfs_mbstoucs(vol_name, &uname); + if (uname_len < 0) + return -errno; + if (uname_len > 128) { + free(uname); + return -ENAMETOOLONG; + } + } + i = insert_resident_attr_in_mft_record(m, AT_VOLUME_NAME, NULL, 0, + CASE_SENSITIVE, const_cpu_to_le16(0), + 0, (u8*)uname, uname_len*sizeof(ntfschar)); + free(uname); + if (i < 0) + ntfs_log_error("add_attr_vol_name failed: %s\n", strerror(-i)); + return i; +} + +/** + * add_attr_vol_info + * + * Return 0 on success or -errno on error. + */ +static int add_attr_vol_info(MFT_RECORD *m, const VOLUME_FLAGS flags, + const u8 major_ver, const u8 minor_ver) +{ + VOLUME_INFORMATION vi; + int err; + + memset(&vi, 0, sizeof(vi)); + vi.major_ver = major_ver; + vi.minor_ver = minor_ver; + vi.flags = flags & VOLUME_FLAGS_MASK; + err = insert_resident_attr_in_mft_record(m, AT_VOLUME_INFORMATION, NULL, + 0, CASE_SENSITIVE, const_cpu_to_le16(0), + 0, (u8*)&vi, sizeof(vi)); + if (err < 0) + ntfs_log_error("add_attr_vol_info failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_index_root + * + * Return 0 on success or -errno on error. + */ +static int add_attr_index_root(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const ATTR_TYPES indexed_attr_type, + const COLLATION_RULES collation_rule, + const u32 index_block_size) +{ + INDEX_ROOT *r; + INDEX_ENTRY_HEADER *e; + int err, val_len; + + val_len = sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER); + r = ntfs_malloc(val_len); + if (!r) + return -errno; + r->type = (indexed_attr_type == AT_FILE_NAME) + ? AT_FILE_NAME : const_cpu_to_le32(0); + if (indexed_attr_type == AT_FILE_NAME && + collation_rule != COLLATION_FILE_NAME) { + free(r); + ntfs_log_error("add_attr_index_root: indexed attribute is $FILE_NAME " + "but collation rule is not COLLATION_FILE_NAME.\n"); + return -EINVAL; + } + r->collation_rule = collation_rule; + r->index_block_size = cpu_to_le32(index_block_size); + if (index_block_size >= g_vol->cluster_size) { + if (index_block_size % g_vol->cluster_size) { + ntfs_log_error("add_attr_index_root: index block size is not " + "a multiple of the cluster size.\n"); + free(r); + return -EINVAL; + } + r->clusters_per_index_block = index_block_size / + g_vol->cluster_size; + } else { /* if (g_vol->cluster_size > index_block_size) */ + if (index_block_size & (index_block_size - 1)) { + ntfs_log_error("add_attr_index_root: index block size is not " + "a power of 2.\n"); + free(r); + return -EINVAL; + } + if (index_block_size < (u32)opts.sector_size) { + ntfs_log_error("add_attr_index_root: index block size " + "is smaller than the sector size.\n"); + free(r); + return -EINVAL; + } + r->clusters_per_index_block = index_block_size + >> NTFS_BLOCK_SIZE_BITS; + } + memset(&r->reserved, 0, sizeof(r->reserved)); + r->index.entries_offset = const_cpu_to_le32(sizeof(INDEX_HEADER)); + r->index.index_length = const_cpu_to_le32(sizeof(INDEX_HEADER) + + sizeof(INDEX_ENTRY_HEADER)); + r->index.allocated_size = r->index.index_length; + r->index.ih_flags = SMALL_INDEX; + memset(&r->index.reserved, 0, sizeof(r->index.reserved)); + e = (INDEX_ENTRY_HEADER*)((u8*)&r->index + + le32_to_cpu(r->index.entries_offset)); + /* + * No matter whether this is a file index or a view as this is a + * termination entry, hence no key value / data is associated with it + * at all. Thus, we just need the union to be all zero. + */ + e->indexed_file = const_cpu_to_le64(0LL); + e->length = const_cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); + e->key_length = const_cpu_to_le16(0); + e->flags = INDEX_ENTRY_END; + e->reserved = const_cpu_to_le16(0); + err = insert_resident_attr_in_mft_record(m, AT_INDEX_ROOT, name, + name_len, ic, const_cpu_to_le16(0), 0, + (u8*)r, val_len); + free(r); + if (err < 0) + ntfs_log_error("add_attr_index_root failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_index_alloc + * + * Return 0 on success or -errno on error. + */ +static int add_attr_index_alloc(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const u8 *index_alloc_val, const u32 index_alloc_val_len) +{ + int err; + + err = insert_non_resident_attr_in_mft_record(m, AT_INDEX_ALLOCATION, + name, name_len, ic, const_cpu_to_le16(0), + index_alloc_val, index_alloc_val_len, WRITE_STANDARD); + if (err < 0) + ntfs_log_error("add_attr_index_alloc failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_bitmap + * + * Return 0 on success or -errno on error. + */ +static int add_attr_bitmap(MFT_RECORD *m, const char *name, const u32 name_len, + const IGNORE_CASE_BOOL ic, const u8 *bitmap, + const u32 bitmap_len) +{ + int err; + + /* Does it fit? NO: create non-resident. YES: create resident. */ + if (le32_to_cpu(m->bytes_in_use) + 24 + bitmap_len > + le32_to_cpu(m->bytes_allocated)) + err = insert_non_resident_attr_in_mft_record(m, AT_BITMAP, name, + name_len, ic, const_cpu_to_le16(0), bitmap, + bitmap_len, WRITE_STANDARD); + else + err = insert_resident_attr_in_mft_record(m, AT_BITMAP, name, + name_len, ic, const_cpu_to_le16(0), 0, + bitmap, bitmap_len); + + if (err < 0) + ntfs_log_error("add_attr_bitmap failed: %s\n", strerror(-err)); + return err; +} + +/** + * add_attr_bitmap_positioned + * + * Create a non-resident bitmap attribute with a predefined on disk location + * specified by the runlist @rl. The clusters specified by @rl are assumed to + * be allocated already. + * + * Return 0 on success or -errno on error. + */ +static int add_attr_bitmap_positioned(MFT_RECORD *m, const char *name, + const u32 name_len, const IGNORE_CASE_BOOL ic, + const runlist *rl, const u8 *bitmap, const u32 bitmap_len) +{ + int err; + + err = insert_positioned_attr_in_mft_record(m, AT_BITMAP, name, name_len, + ic, const_cpu_to_le16(0), rl, bitmap, bitmap_len); + if (err < 0) + ntfs_log_error("add_attr_bitmap_positioned failed: %s\n", + strerror(-err)); + return err; +} + + +/** + * upgrade_to_large_index + * + * Create bitmap and index allocation attributes, modify index root + * attribute accordingly and move all of the index entries from the index root + * into the index allocation. + * + * Return 0 on success or -errno on error. + */ +static int upgrade_to_large_index(MFT_RECORD *m, const char *name, + u32 name_len, const IGNORE_CASE_BOOL ic, + INDEX_ALLOCATION **idx) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + INDEX_ROOT *r; + INDEX_ENTRY *re; + INDEX_ALLOCATION *ia_val = NULL; + ntfschar *uname = NULL; + int uname_len = 0; + u8 bmp[8]; + char *re_start, *re_end; + int i, err, index_block_size; + + uname = ntfs_str2ucs(name, &uname_len); + if (!uname) + return -errno; + + /* Find the index root attribute. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search context.\n"); + ntfs_ucsfree(uname); + return -ENOMEM; + } + if (ic == IGNORE_CASE) { + ntfs_log_error("FIXME: Hit unimplemented code path #4.\n"); + err = -EOPNOTSUPP; + ntfs_ucsfree(uname); + goto err_out; + } + err = mkntfs_attr_lookup(AT_INDEX_ROOT, uname, uname_len, ic, 0, NULL, 0, + ctx); + ntfs_ucsfree(uname); + if (err) { + err = -ENOTDIR; + goto err_out; + } + a = ctx->attr; + if (a->non_resident || a->flags) { + err = -EINVAL; + goto err_out; + } + r = (INDEX_ROOT*)((char*)a + le16_to_cpu(a->value_offset)); + re_end = (char*)r + le32_to_cpu(a->value_length); + re_start = (char*)&r->index + le32_to_cpu(r->index.entries_offset); + re = (INDEX_ENTRY*)re_start; + index_block_size = le32_to_cpu(r->index_block_size); + memset(bmp, 0, sizeof(bmp)); + ntfs_bit_set(bmp, 0ULL, 1); + /* Bitmap has to be at least 8 bytes in size. */ + err = add_attr_bitmap(m, name, name_len, ic, bmp, sizeof(bmp)); + if (err) + goto err_out; + ia_val = ntfs_calloc(index_block_size); + if (!ia_val) { + err = -errno; + goto err_out; + } + /* Setup header. */ + ia_val->magic = magic_INDX; + ia_val->usa_ofs = cpu_to_le16(sizeof(INDEX_ALLOCATION)); + if (index_block_size >= NTFS_BLOCK_SIZE) { + ia_val->usa_count = cpu_to_le16(index_block_size / + NTFS_BLOCK_SIZE + 1); + } else { + ia_val->usa_count = cpu_to_le16(1); + ntfs_log_error("Sector size is bigger than index block size. " + "Setting usa_count to 1. If Windows chkdsk " + "reports this as corruption, please email %s " + "stating that you saw this message and that " + "the filesystem created was corrupt. " + "Thank you.", NTFS_DEV_LIST); + } + /* Set USN to 1. */ + *(le16*)((char*)ia_val + le16_to_cpu(ia_val->usa_ofs)) = + cpu_to_le16(1); + ia_val->lsn = cpu_to_le64(0); + ia_val->index_block_vcn = cpu_to_le64(0); + ia_val->index.ih_flags = LEAF_NODE; + /* Align to 8-byte boundary. */ + ia_val->index.entries_offset = cpu_to_le32((sizeof(INDEX_HEADER) + + le16_to_cpu(ia_val->usa_count) * 2 + 7) & ~7); + ia_val->index.allocated_size = cpu_to_le32(index_block_size - + (sizeof(INDEX_ALLOCATION) - sizeof(INDEX_HEADER))); + /* Find the last entry in the index root and save it in re. */ + while ((char*)re < re_end && !(re->ie_flags & INDEX_ENTRY_END)) { + /* Next entry in index root. */ + re = (INDEX_ENTRY*)((char*)re + le16_to_cpu(re->length)); + } + /* Copy all the entries including the termination entry. */ + i = (char*)re - re_start + le16_to_cpu(re->length); + memcpy((char*)&ia_val->index + + le32_to_cpu(ia_val->index.entries_offset), re_start, i); + /* Finish setting up index allocation. */ + ia_val->index.index_length = cpu_to_le32(i + + le32_to_cpu(ia_val->index.entries_offset)); + /* Move the termination entry forward to the beginning if necessary. */ + if ((char*)re > re_start) { + memmove(re_start, (char*)re, le16_to_cpu(re->length)); + re = (INDEX_ENTRY*)re_start; + } + /* Now fixup empty index root with pointer to index allocation VCN 0. */ + r->index.ih_flags = LARGE_INDEX; + re->ie_flags |= INDEX_ENTRY_NODE; + if (le16_to_cpu(re->length) < sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)) + re->length = cpu_to_le16(le16_to_cpu(re->length) + sizeof(VCN)); + r->index.index_length = cpu_to_le32(le32_to_cpu(r->index.entries_offset) + + le16_to_cpu(re->length)); + r->index.allocated_size = r->index.index_length; + /* Resize index root attribute. */ + if (ntfs_resident_attr_value_resize(m, a, sizeof(INDEX_ROOT) - + sizeof(INDEX_HEADER) + + le32_to_cpu(r->index.allocated_size))) { + /* TODO: Remove the added bitmap! */ + /* Revert index root from index allocation. */ + err = -errno; + goto err_out; + } + /* Set VCN pointer to 0LL. */ + *(leVCN*)((char*)re + cpu_to_le16(re->length) - sizeof(VCN)) = + cpu_to_le64(0); + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)ia_val, index_block_size); + if (err) { + err = -errno; + ntfs_log_error("ntfs_mst_pre_write_fixup() failed in " + "upgrade_to_large_index.\n"); + goto err_out; + } + err = add_attr_index_alloc(m, name, name_len, ic, (u8*)ia_val, + index_block_size); + ntfs_mst_post_write_fixup((NTFS_RECORD*)ia_val); + if (err) { + /* TODO: Remove the added bitmap! */ + /* Revert index root from index allocation. */ + goto err_out; + } + *idx = ia_val; + ntfs_attr_put_search_ctx(ctx); + return 0; +err_out: + ntfs_attr_put_search_ctx(ctx); + free(ia_val); + return err; +} + +/** + * make_room_for_index_entry_in_index_block + * + * Create space of @size bytes at position @pos inside the index block @idx. + * + * Return 0 on success or -errno on error. + */ +static int make_room_for_index_entry_in_index_block(INDEX_BLOCK *idx, + INDEX_ENTRY *pos, u32 size) +{ + u32 biu; + + if (!size) + return 0; +#ifdef DEBUG + /* + * Rigorous consistency checks. Always return -EINVAL even if more + * appropriate codes exist for simplicity of parsing the return value. + */ + if (size != ((size + 7) & ~7)) { + ntfs_log_error("make_room_for_index_entry_in_index_block() received " + "non 8-byte aligned size.\n"); + return -EINVAL; + } + if (!idx || !pos) + return -EINVAL; + if ((char*)pos < (char*)idx || (char*)pos + size < (char*)idx || + (char*)pos > (char*)idx + sizeof(INDEX_BLOCK) - + sizeof(INDEX_HEADER) + + le32_to_cpu(idx->index.allocated_size) || + (char*)pos + size > (char*)idx + sizeof(INDEX_BLOCK) - + sizeof(INDEX_HEADER) + + le32_to_cpu(idx->index.allocated_size)) + return -EINVAL; + /* The - sizeof(INDEX_ENTRY_HEADER) is for the index terminator. */ + if ((char*)pos - (char*)&idx->index > + (int)le32_to_cpu(idx->index.index_length) + - (int)sizeof(INDEX_ENTRY_HEADER)) + return -EINVAL; +#endif + biu = le32_to_cpu(idx->index.index_length); + /* Do we have enough space? */ + if (biu + size > le32_to_cpu(idx->index.allocated_size)) + return -ENOSPC; + /* Move everything after pos to pos + size. */ + memmove((char*)pos + size, (char*)pos, biu - ((char*)pos - + (char*)&idx->index)); + /* Update index block. */ + idx->index.index_length = cpu_to_le32(biu + size); + return 0; +} + +/** + * ntfs_index_keys_compare + * + * not all types of COLLATION_RULES supported yet... + * added as needed.. (remove this comment when all are added) + */ +static int ntfs_index_keys_compare(u8 *key1, u8 *key2, int key1_length, + int key2_length, COLLATION_RULES collation_rule) +{ + u32 u1, u2; + int i; + + if (collation_rule == COLLATION_NTOFS_ULONG) { + /* i.e. $SII or $QUOTA-$Q */ + u1 = le32_to_cpup((const le32*)key1); + u2 = le32_to_cpup((const le32*)key2); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + /* u1 == u2 */ + return 0; + } + if (collation_rule == COLLATION_NTOFS_ULONGS) { + /* i.e $OBJID-$O */ + i = 0; + while (i < min(key1_length, key2_length)) { + u1 = le32_to_cpup((const le32*)(key1 + i)); + u2 = le32_to_cpup((const le32*)(key2 + i)); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + /* u1 == u2 */ + i += sizeof(u32); + } + if (key1_length < key2_length) + return -1; + if (key1_length > key2_length) + return 1; + return 0; + } + if (collation_rule == COLLATION_NTOFS_SECURITY_HASH) { + /* i.e. $SDH */ + u1 = le32_to_cpu(((SDH_INDEX_KEY*)key1)->hash); + u2 = le32_to_cpu(((SDH_INDEX_KEY*)key2)->hash); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + /* u1 == u2 */ + u1 = le32_to_cpu(((SDH_INDEX_KEY*)key1)->security_id); + u2 = le32_to_cpu(((SDH_INDEX_KEY*)key2)->security_id); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + return 0; + } + if (collation_rule == COLLATION_NTOFS_SID) { + /* i.e. $QUOTA-O */ + i = memcmp(key1, key2, min(key1_length, key2_length)); + if (!i) { + if (key1_length < key2_length) + return -1; + if (key1_length > key2_length) + return 1; + } + return i; + } + ntfs_log_critical("ntfs_index_keys_compare called without supported " + "collation rule.\n"); + return 0; /* Claim they're equal. What else can we do? */ +} + +/** + * insert_index_entry_in_res_dir_index + * + * i.e. insert an index_entry in some named index_root + * simplified search method, works for mkntfs + */ +static int insert_index_entry_in_res_dir_index(INDEX_ENTRY *idx, u32 idx_size, + MFT_RECORD *m, ntfschar *name, u32 name_size, ATTR_TYPES type) +{ + ntfs_attr_search_ctx *ctx; + INDEX_HEADER *idx_header; + INDEX_ENTRY *idx_entry, *idx_end; + ATTR_RECORD *a; + COLLATION_RULES collation_rule; + int err, i; + + err = 0; + /* does it fit ?*/ + if (g_vol->mft_record_size > idx_size + le32_to_cpu(m->bytes_allocated)) + return -ENOSPC; + /* find the INDEX_ROOT attribute:*/ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_error("Failed to allocate attribute search " + "context.\n"); + err = -ENOMEM; + goto err_out; + } + if (mkntfs_attr_lookup(AT_INDEX_ROOT, name, name_size, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + err = -EEXIST; + goto err_out; + } + /* found attribute */ + a = (ATTR_RECORD*)ctx->attr; + collation_rule = ((INDEX_ROOT*)((u8*)a + + le16_to_cpu(a->value_offset)))->collation_rule; + idx_header = (INDEX_HEADER*)((u8*)a + le16_to_cpu(a->value_offset) + + 0x10); + idx_entry = (INDEX_ENTRY*)((u8*)idx_header + + le32_to_cpu(idx_header->entries_offset)); + idx_end = (INDEX_ENTRY*)((u8*)idx_entry + + le32_to_cpu(idx_header->index_length)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + if (type == AT_FILE_NAME) { + while (((u8*)idx_entry < (u8*)idx_end) && + !(idx_entry->ie_flags & INDEX_ENTRY_END)) { + /* + i = ntfs_file_values_compare(&idx->key.file_name, + &idx_entry->key.file_name, 1, + IGNORE_CASE, g_vol->upcase, + g_vol->upcase_len); + */ + i = ntfs_names_full_collate(idx->key.file_name.file_name, idx->key.file_name.file_name_length, + idx_entry->key.file_name.file_name, idx_entry->key.file_name.file_name_length, + IGNORE_CASE, g_vol->upcase, + g_vol->upcase_len); + /* + * If @file_name collates before ie->key.file_name, + * there is no matching index entry. + */ + if (i == -1) + break; + /* If file names are not equal, continue search. */ + if (i) + goto do_next; + if (idx->key.file_name.file_name_type != + FILE_NAME_POSIX || + idx_entry->key.file_name.file_name_type + != FILE_NAME_POSIX) + return -EEXIST; + /* + i = ntfs_file_values_compare(&idx->key.file_name, + &idx_entry->key.file_name, 1, + CASE_SENSITIVE, g_vol->upcase, + g_vol->upcase_len); + */ + i = ntfs_names_full_collate(idx->key.file_name.file_name, idx->key.file_name.file_name_length, + idx_entry->key.file_name.file_name, idx_entry->key.file_name.file_name_length, + CASE_SENSITIVE, g_vol->upcase, + g_vol->upcase_len); + if (!i) + return -EEXIST; + if (i == -1) + break; +do_next: + idx_entry = (INDEX_ENTRY*)((u8*)idx_entry + + le16_to_cpu(idx_entry->length)); + } + } else if (type == AT_UNUSED) { /* case view */ + while (((u8*)idx_entry < (u8*)idx_end) && + !(idx_entry->ie_flags & INDEX_ENTRY_END)) { + i = ntfs_index_keys_compare((u8*)idx + 0x10, + (u8*)idx_entry + 0x10, + le16_to_cpu(idx->key_length), + le16_to_cpu(idx_entry->key_length), + collation_rule); + if (!i) + return -EEXIST; + if (i == -1) + break; + idx_entry = (INDEX_ENTRY*)((u8*)idx_entry + + le16_to_cpu(idx_entry->length)); + } + } else + return -EINVAL; + memmove((u8*)idx_entry + idx_size, (u8*)idx_entry, + le32_to_cpu(m->bytes_in_use) - + ((u8*)idx_entry - (u8*)m)); + memcpy((u8*)idx_entry, (u8*)idx, idx_size); + /* Adjust various offsets, etc... */ + m->bytes_in_use = cpu_to_le32(le32_to_cpu(m->bytes_in_use) + idx_size); + a->length = cpu_to_le32(le32_to_cpu(a->length) + idx_size); + a->value_length = cpu_to_le32(le32_to_cpu(a->value_length) + idx_size); + idx_header->index_length = cpu_to_le32( + le32_to_cpu(idx_header->index_length) + idx_size); + idx_header->allocated_size = cpu_to_le32( + le32_to_cpu(idx_header->allocated_size) + idx_size); +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return err; +} + +/** + * initialize_secure + * + * initializes $Secure's $SDH and $SII indexes from $SDS datastream + */ +static int initialize_secure(char *sds, u32 sds_size, MFT_RECORD *m) +{ + int err, sdh_size, sii_size; + SECURITY_DESCRIPTOR_HEADER *sds_header; + INDEX_ENTRY *idx_entry_sdh, *idx_entry_sii; + SDH_INDEX_DATA *sdh_data; + SII_INDEX_DATA *sii_data; + + sds_header = (SECURITY_DESCRIPTOR_HEADER*)sds; + sdh_size = sizeof(INDEX_ENTRY_HEADER); + sdh_size += sizeof(SDH_INDEX_KEY) + sizeof(SDH_INDEX_DATA); + sii_size = sizeof(INDEX_ENTRY_HEADER); + sii_size += sizeof(SII_INDEX_KEY) + sizeof(SII_INDEX_DATA); + idx_entry_sdh = ntfs_calloc(sizeof(INDEX_ENTRY)); + if (!idx_entry_sdh) + return -errno; + idx_entry_sii = ntfs_calloc(sizeof(INDEX_ENTRY)); + if (!idx_entry_sii) { + free(idx_entry_sdh); + return -errno; + } + err = 0; + + while ((char*)sds_header < (char*)sds + sds_size) { + if (!sds_header->length) + break; + /* SDH index entry */ + idx_entry_sdh->data_offset = const_cpu_to_le16(0x18); + idx_entry_sdh->data_length = const_cpu_to_le16(0x14); + idx_entry_sdh->reservedV = const_cpu_to_le32(0x00); + idx_entry_sdh->length = const_cpu_to_le16(0x30); + idx_entry_sdh->key_length = const_cpu_to_le16(0x08); + idx_entry_sdh->ie_flags = const_cpu_to_le16(0x00); + idx_entry_sdh->reserved = const_cpu_to_le16(0x00); + idx_entry_sdh->key.sdh.hash = sds_header->hash; + idx_entry_sdh->key.sdh.security_id = sds_header->security_id; + sdh_data = (SDH_INDEX_DATA*)((u8*)idx_entry_sdh + + le16_to_cpu(idx_entry_sdh->data_offset)); + sdh_data->hash = sds_header->hash; + sdh_data->security_id = sds_header->security_id; + sdh_data->offset = sds_header->offset; + sdh_data->length = sds_header->length; + sdh_data->reserved_II = const_cpu_to_le32(0x00490049); + + /* SII index entry */ + idx_entry_sii->data_offset = const_cpu_to_le16(0x14); + idx_entry_sii->data_length = const_cpu_to_le16(0x14); + idx_entry_sii->reservedV = const_cpu_to_le32(0x00); + idx_entry_sii->length = const_cpu_to_le16(0x28); + idx_entry_sii->key_length = const_cpu_to_le16(0x04); + idx_entry_sii->ie_flags = const_cpu_to_le16(0x00); + idx_entry_sii->reserved = const_cpu_to_le16(0x00); + idx_entry_sii->key.sii.security_id = sds_header->security_id; + sii_data = (SII_INDEX_DATA*)((u8*)idx_entry_sii + + le16_to_cpu(idx_entry_sii->data_offset)); + sii_data->hash = sds_header->hash; + sii_data->security_id = sds_header->security_id; + sii_data->offset = sds_header->offset; + sii_data->length = sds_header->length; + if ((err = insert_index_entry_in_res_dir_index(idx_entry_sdh, + sdh_size, m, NTFS_INDEX_SDH, 4, AT_UNUSED))) + break; + if ((err = insert_index_entry_in_res_dir_index(idx_entry_sii, + sii_size, m, NTFS_INDEX_SII, 4, AT_UNUSED))) + break; + sds_header = (SECURITY_DESCRIPTOR_HEADER*)((u8*)sds_header + + ((le32_to_cpu(sds_header->length) + 15) & ~15)); + } + free(idx_entry_sdh); + free(idx_entry_sii); + return err; +} + +/** + * initialize_quota + * + * initialize $Quota with the default quota index-entries. + */ +static int initialize_quota(MFT_RECORD *m) +{ + int o_size, q1_size, q2_size, err, i; + INDEX_ENTRY *idx_entry_o, *idx_entry_q1, *idx_entry_q2; + QUOTA_O_INDEX_DATA *idx_entry_o_data; + QUOTA_CONTROL_ENTRY *idx_entry_q1_data, *idx_entry_q2_data; + + err = 0; + /* q index entry num 1 */ + q1_size = 0x48; + idx_entry_q1 = ntfs_calloc(q1_size); + if (!idx_entry_q1) + return errno; + idx_entry_q1->data_offset = const_cpu_to_le16(0x14); + idx_entry_q1->data_length = const_cpu_to_le16(0x30); + idx_entry_q1->reservedV = const_cpu_to_le32(0x00); + idx_entry_q1->length = const_cpu_to_le16(0x48); + idx_entry_q1->key_length = const_cpu_to_le16(0x04); + idx_entry_q1->ie_flags = const_cpu_to_le16(0x00); + idx_entry_q1->reserved = const_cpu_to_le16(0x00); + idx_entry_q1->key.owner_id = const_cpu_to_le32(0x01); + idx_entry_q1_data = (QUOTA_CONTROL_ENTRY*)((char*)idx_entry_q1 + + le16_to_cpu(idx_entry_q1->data_offset)); + idx_entry_q1_data->version = const_cpu_to_le32(0x02); + idx_entry_q1_data->flags = QUOTA_FLAG_DEFAULT_LIMITS; + idx_entry_q1_data->bytes_used = const_cpu_to_le64(0x00); + idx_entry_q1_data->change_time = mkntfs_time(); + idx_entry_q1_data->threshold = cpu_to_sle64(-1); + idx_entry_q1_data->limit = cpu_to_sle64(-1); + idx_entry_q1_data->exceeded_time = const_cpu_to_le64(0); + err = insert_index_entry_in_res_dir_index(idx_entry_q1, q1_size, m, + NTFS_INDEX_Q, 2, AT_UNUSED); + free(idx_entry_q1); + if (err) + return err; + /* q index entry num 2 */ + q2_size = 0x58; + idx_entry_q2 = ntfs_calloc(q2_size); + if (!idx_entry_q2) + return errno; + idx_entry_q2->data_offset = const_cpu_to_le16(0x14); + idx_entry_q2->data_length = const_cpu_to_le16(0x40); + idx_entry_q2->reservedV = const_cpu_to_le32(0x00); + idx_entry_q2->length = const_cpu_to_le16(0x58); + idx_entry_q2->key_length = const_cpu_to_le16(0x04); + idx_entry_q2->ie_flags = const_cpu_to_le16(0x00); + idx_entry_q2->reserved = const_cpu_to_le16(0x00); + idx_entry_q2->key.owner_id = QUOTA_FIRST_USER_ID; + idx_entry_q2_data = (QUOTA_CONTROL_ENTRY*)((char*)idx_entry_q2 + + le16_to_cpu(idx_entry_q2->data_offset)); + idx_entry_q2_data->version = const_cpu_to_le32(0x02); + idx_entry_q2_data->flags = QUOTA_FLAG_DEFAULT_LIMITS; + idx_entry_q2_data->bytes_used = const_cpu_to_le64(0x00); + idx_entry_q2_data->change_time = mkntfs_time(); + idx_entry_q2_data->threshold = cpu_to_sle64(-1); + idx_entry_q2_data->limit = cpu_to_sle64(-1); + idx_entry_q2_data->exceeded_time = const_cpu_to_le64(0); + idx_entry_q2_data->sid.revision = 1; + idx_entry_q2_data->sid.sub_authority_count = 2; + for (i = 0; i < 5; i++) + idx_entry_q2_data->sid.identifier_authority.value[i] = 0; + idx_entry_q2_data->sid.identifier_authority.value[5] = 0x05; + idx_entry_q2_data->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + idx_entry_q2_data->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + err = insert_index_entry_in_res_dir_index(idx_entry_q2, q2_size, m, + NTFS_INDEX_Q, 2, AT_UNUSED); + free(idx_entry_q2); + if (err) + return err; + o_size = 0x28; + idx_entry_o = ntfs_calloc(o_size); + if (!idx_entry_o) + return errno; + idx_entry_o->data_offset = const_cpu_to_le16(0x20); + idx_entry_o->data_length = const_cpu_to_le16(0x04); + idx_entry_o->reservedV = const_cpu_to_le32(0x00); + idx_entry_o->length = const_cpu_to_le16(0x28); + idx_entry_o->key_length = const_cpu_to_le16(0x10); + idx_entry_o->ie_flags = const_cpu_to_le16(0x00); + idx_entry_o->reserved = const_cpu_to_le16(0x00); + idx_entry_o->key.sid.revision = 0x01; + idx_entry_o->key.sid.sub_authority_count = 0x02; + for (i = 0; i < 5; i++) + idx_entry_o->key.sid.identifier_authority.value[i] = 0; + idx_entry_o->key.sid.identifier_authority.value[5] = 0x05; + idx_entry_o->key.sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + idx_entry_o->key.sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + idx_entry_o_data = (QUOTA_O_INDEX_DATA*)((char*)idx_entry_o + + le16_to_cpu(idx_entry_o->data_offset)); + idx_entry_o_data->owner_id = QUOTA_FIRST_USER_ID; + /* 20 00 00 00 padding after here on ntfs 3.1. 3.0 is unchecked. */ + idx_entry_o_data->unknown = const_cpu_to_le32(32); + err = insert_index_entry_in_res_dir_index(idx_entry_o, o_size, m, + NTFS_INDEX_O, 2, AT_UNUSED); + free(idx_entry_o); + + return err; +} + +/** + * insert_file_link_in_dir_index + * + * Insert the fully completed FILE_NAME_ATTR @file_name which is inside + * the file with mft reference @file_ref into the index (allocation) block + * @idx (which belongs to @file_ref's parent directory). + * + * Return 0 on success or -errno on error. + */ +static int insert_file_link_in_dir_index(INDEX_BLOCK *idx, leMFT_REF file_ref, + FILE_NAME_ATTR *file_name, u32 file_name_size) +{ + int err, i; + INDEX_ENTRY *ie; + char *index_end; + + /* + * Lookup dir entry @file_name in dir @idx to determine correct + * insertion location. FIXME: Using a very oversimplified lookup + * method which is sufficient for mkntfs but no good whatsoever in + * real world scenario. (AIA) + */ + + index_end = (char*)&idx->index + le32_to_cpu(idx->index.index_length); + ie = (INDEX_ENTRY*)((char*)&idx->index + + le32_to_cpu(idx->index.entries_offset)); + /* + * Loop until we exceed valid memory (corruption case) or until we + * reach the last entry. + */ + while ((char*)ie < index_end && !(ie->ie_flags & INDEX_ENTRY_END)) { +#if 0 +#ifdef DEBUG + ntfs_log_debug("file_name_attr1->file_name_length = %i\n", + file_name->file_name_length); + if (file_name->file_name_length) { + char *__buf = NULL; + i = ntfs_ucstombs((ntfschar*)&file_name->file_name, + file_name->file_name_length, &__buf, 0); + if (i < 0) + ntfs_log_debug("Name contains non-displayable " + "Unicode characters.\n"); + ntfs_log_debug("file_name_attr1->file_name = %s\n", + __buf); + free(__buf); + } + ntfs_log_debug("file_name_attr2->file_name_length = %i\n", + ie->key.file_name.file_name_length); + if (ie->key.file_name.file_name_length) { + char *__buf = NULL; + i = ntfs_ucstombs(ie->key.file_name.file_name, + ie->key.file_name.file_name_length + 1, &__buf, + 0); + if (i < 0) + ntfs_log_debug("Name contains non-displayable " + "Unicode characters.\n"); + ntfs_log_debug("file_name_attr2->file_name = %s\n", + __buf); + free(__buf); + } +#endif +#endif + /* + i = ntfs_file_values_compare(file_name, + (FILE_NAME_ATTR*)&ie->key.file_name, 1, + IGNORE_CASE, g_vol->upcase, g_vol->upcase_len); + */ + i = ntfs_names_full_collate(file_name->file_name, file_name->file_name_length, + ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name, ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name_length, + IGNORE_CASE, g_vol->upcase, g_vol->upcase_len); + /* + * If @file_name collates before ie->key.file_name, there is no + * matching index entry. + */ + if (i == -1) + break; + /* If file names are not equal, continue search. */ + if (i) + goto do_next; + /* File names are equal when compared ignoring case. */ + /* + * If BOTH file names are in the POSIX namespace, do a case + * sensitive comparison as well. Otherwise the names match so + * we return -EEXIST. FIXME: There are problems with this in a + * real world scenario, when one is POSIX and one isn't, but + * fine for mkntfs where we don't use POSIX namespace at all + * and hence this following code is luxury. (AIA) + */ + if (file_name->file_name_type != FILE_NAME_POSIX || + ie->key.file_name.file_name_type != FILE_NAME_POSIX) + return -EEXIST; + /* + i = ntfs_file_values_compare(file_name, + (FILE_NAME_ATTR*)&ie->key.file_name, 1, + CASE_SENSITIVE, g_vol->upcase, + g_vol->upcase_len); + */ + i = ntfs_names_full_collate(file_name->file_name, file_name->file_name_length, + ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name, ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name_length, + CASE_SENSITIVE, g_vol->upcase, g_vol->upcase_len); + if (i == -1) + break; + /* Complete match. Bugger. Can't insert. */ + if (!i) + return -EEXIST; +do_next: +#ifdef DEBUG + /* Next entry. */ + if (!ie->length) { + ntfs_log_debug("BUG: ie->length is zero, breaking out " + "of loop.\n"); + break; + } +#endif + ie = (INDEX_ENTRY*)((char*)ie + le16_to_cpu(ie->length)); + }; + i = (sizeof(INDEX_ENTRY_HEADER) + file_name_size + 7) & ~7; + err = make_room_for_index_entry_in_index_block(idx, ie, i); + if (err) { + ntfs_log_error("make_room_for_index_entry_in_index_block " + "failed: %s\n", strerror(-err)); + return err; + } + /* Create entry in place and copy file name attribute value. */ + ie->indexed_file = file_ref; + ie->length = cpu_to_le16(i); + ie->key_length = cpu_to_le16(file_name_size); + ie->ie_flags = cpu_to_le16(0); + ie->reserved = cpu_to_le16(0); + memcpy((char*)&ie->key.file_name, (char*)file_name, file_name_size); + return 0; +} + +/** + * create_hardlink_res + * + * Create a file_name_attribute in the mft record @m_file which points to the + * parent directory with mft reference @ref_parent. + * + * Then, insert an index entry with this file_name_attribute in the index + * root @idx of the index_root attribute of the parent directory. + * + * @ref_file is the mft reference of @m_file. + * + * Return 0 on success or -errno on error. + */ +static int create_hardlink_res(MFT_RECORD *m_parent, const leMFT_REF ref_parent, + MFT_RECORD *m_file, const leMFT_REF ref_file, + const s64 allocated_size, const s64 data_size, + const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, + const u32 reparse_point_tag, const char *file_name, + const FILE_NAME_TYPE_FLAGS file_name_type) +{ + FILE_NAME_ATTR *fn; + int i, fn_size, idx_size; + INDEX_ENTRY *idx_entry_new; + ntfschar *uname; + + /* Create the file_name attribute. */ + i = (strlen(file_name) + 1) * sizeof(ntfschar); + fn_size = sizeof(FILE_NAME_ATTR) + i; + fn = ntfs_malloc(fn_size); + if (!fn) + return -errno; + fn->parent_directory = ref_parent; + fn->creation_time = stdinfo_time(m_file); + fn->last_data_change_time = fn->creation_time; + fn->last_mft_change_time = fn->creation_time; + fn->last_access_time = fn->creation_time; + fn->allocated_size = cpu_to_sle64(allocated_size); + fn->data_size = cpu_to_sle64(data_size); + fn->file_attributes = flags; + /* These are in a union so can't have both. */ + if (packed_ea_size && reparse_point_tag) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + fn->packed_ea_size = cpu_to_le16(packed_ea_size); + fn->reserved = cpu_to_le16(0); + } else { + fn->reparse_point_tag = cpu_to_le32(reparse_point_tag); + } + fn->file_name_type = file_name_type; + uname = fn->file_name; + i = ntfs_mbstoucs_libntfscompat(file_name, &uname, i); + if (i < 1) { + free(fn); + return -EINVAL; + } + if (i > 0xff) { + free(fn); + return -ENAMETOOLONG; + } + /* No terminating null in file names. */ + fn->file_name_length = i; + fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); + /* Increment the link count of @m_file. */ + i = le16_to_cpu(m_file->link_count); + if (i == 0xffff) { + ntfs_log_error("Too many hardlinks present already.\n"); + free(fn); + return -EINVAL; + } + m_file->link_count = cpu_to_le16(i + 1); + /* Add the file_name to @m_file. */ + i = insert_resident_attr_in_mft_record(m_file, AT_FILE_NAME, NULL, 0, + CASE_SENSITIVE, const_cpu_to_le16(0), + RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); + if (i < 0) { + ntfs_log_error("create_hardlink failed adding file name " + "attribute: %s\n", strerror(-i)); + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + /* Insert the index entry for file_name in @idx. */ + idx_size = (fn_size + 7) & ~7; + idx_entry_new = ntfs_calloc(idx_size + 0x10); + if (!idx_entry_new) + return -errno; + idx_entry_new->indexed_file = ref_file; + idx_entry_new->length = cpu_to_le16(idx_size + 0x10); + idx_entry_new->key_length = cpu_to_le16(fn_size); + memcpy((u8*)idx_entry_new + 0x10, (u8*)fn, fn_size); + i = insert_index_entry_in_res_dir_index(idx_entry_new, idx_size + 0x10, + m_parent, NTFS_INDEX_I30, 4, AT_FILE_NAME); + if (i < 0) { + ntfs_log_error("create_hardlink failed inserting index entry: " + "%s\n", strerror(-i)); + /* FIXME: Remove the file name attribute from @m_file. */ + free(idx_entry_new); + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + free(idx_entry_new); + free(fn); + return 0; +} + +/** + * create_hardlink + * + * Create a file_name_attribute in the mft record @m_file which points to the + * parent directory with mft reference @ref_parent. + * + * Then, insert an index entry with this file_name_attribute in the index + * block @idx of the index allocation attribute of the parent directory. + * + * @ref_file is the mft reference of @m_file. + * + * Return 0 on success or -errno on error. + */ +static int create_hardlink(INDEX_BLOCK *idx, const leMFT_REF ref_parent, + MFT_RECORD *m_file, const leMFT_REF ref_file, + const s64 allocated_size, const s64 data_size, + const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, + const u32 reparse_point_tag, const char *file_name, + const FILE_NAME_TYPE_FLAGS file_name_type) +{ + FILE_NAME_ATTR *fn; + int i, fn_size; + ntfschar *uname; + + /* Create the file_name attribute. */ + i = (strlen(file_name) + 1) * sizeof(ntfschar); + fn_size = sizeof(FILE_NAME_ATTR) + i; + fn = ntfs_malloc(fn_size); + if (!fn) + return -errno; + fn->parent_directory = ref_parent; + fn->creation_time = stdinfo_time(m_file); + fn->last_data_change_time = fn->creation_time; + fn->last_mft_change_time = fn->creation_time; + fn->last_access_time = fn->creation_time; + /* allocated size depends on unnamed data being resident */ + if (allocated_size && non_resident_unnamed_data(m_file)) + fn->allocated_size = cpu_to_sle64(allocated_size); + else + fn->allocated_size = cpu_to_sle64((data_size + 7) & -8); + fn->data_size = cpu_to_sle64(data_size); + fn->file_attributes = flags; + /* These are in a union so can't have both. */ + if (packed_ea_size && reparse_point_tag) { + free(fn); + return -EINVAL; + } + if (packed_ea_size) { + fn->packed_ea_size = cpu_to_le16(packed_ea_size); + fn->reserved = cpu_to_le16(0); + } else { + fn->reparse_point_tag = cpu_to_le32(reparse_point_tag); + } + fn->file_name_type = file_name_type; + uname = fn->file_name; + i = ntfs_mbstoucs_libntfscompat(file_name, &uname, i); + if (i < 1) { + free(fn); + return -EINVAL; + } + if (i > 0xff) { + free(fn); + return -ENAMETOOLONG; + } + /* No terminating null in file names. */ + fn->file_name_length = i; + fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); + /* Increment the link count of @m_file. */ + i = le16_to_cpu(m_file->link_count); + if (i == 0xffff) { + ntfs_log_error("Too many hardlinks present already.\n"); + free(fn); + return -EINVAL; + } + m_file->link_count = cpu_to_le16(i + 1); + /* Add the file_name to @m_file. */ + i = insert_resident_attr_in_mft_record(m_file, AT_FILE_NAME, NULL, 0, + CASE_SENSITIVE, cpu_to_le16(0), + RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); + if (i < 0) { + ntfs_log_error("create_hardlink failed adding file name attribute: " + "%s\n", strerror(-i)); + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + /* Insert the index entry for file_name in @idx. */ + i = insert_file_link_in_dir_index(idx, ref_file, fn, fn_size); + if (i < 0) { + ntfs_log_error("create_hardlink failed inserting index entry: %s\n", + strerror(-i)); + /* FIXME: Remove the file name attribute from @m_file. */ + free(fn); + /* Undo link count increment. */ + m_file->link_count = cpu_to_le16( + le16_to_cpu(m_file->link_count) - 1); + return i; + } + free(fn); + return 0; +} + +/** + * index_obj_id_insert + * + * Insert an index entry with the key @guid and data pointing to the mft record + * @ref in the $O index root of the mft record @m (which must be the mft record + * for $ObjId). + * + * Return 0 on success or -errno on error. + */ +static int index_obj_id_insert(MFT_RECORD *m, const GUID *guid, + const leMFT_REF ref) +{ + INDEX_ENTRY *idx_entry_new; + int data_ofs, idx_size, err; + OBJ_ID_INDEX_DATA *oi; + + /* + * Insert the index entry for the object id in the index. + * + * First determine the size of the index entry to be inserted. This + * consists of the index entry header, followed by the index key, i.e. + * the GUID, followed by the index data, i.e. OBJ_ID_INDEX_DATA. + */ + data_ofs = (sizeof(INDEX_ENTRY_HEADER) + sizeof(GUID) + 7) & ~7; + idx_size = (data_ofs + sizeof(OBJ_ID_INDEX_DATA) + 7) & ~7; + idx_entry_new = ntfs_calloc(idx_size); + if (!idx_entry_new) + return -errno; + idx_entry_new->data_offset = cpu_to_le16(data_ofs); + idx_entry_new->data_length = cpu_to_le16(sizeof(OBJ_ID_INDEX_DATA)); + idx_entry_new->length = cpu_to_le16(idx_size); + idx_entry_new->key_length = cpu_to_le16(sizeof(GUID)); + idx_entry_new->key.object_id = *guid; + oi = (OBJ_ID_INDEX_DATA*)((u8*)idx_entry_new + data_ofs); + oi->mft_reference = ref; + err = insert_index_entry_in_res_dir_index(idx_entry_new, idx_size, m, + NTFS_INDEX_O, 2, AT_UNUSED); + free(idx_entry_new); + if (err < 0) { + ntfs_log_error("index_obj_id_insert failed inserting index " + "entry: %s\n", strerror(-err)); + return err; + } + return 0; +} + +/** + * mkntfs_cleanup + */ +static void mkntfs_cleanup(void) +{ + struct BITMAP_ALLOCATION *p, *q; + + /* Close the volume */ + if (g_vol) { + if (g_vol->dev) { + if (NDevOpen(g_vol->dev) && g_vol->dev->d_ops->close(g_vol->dev)) + ntfs_log_perror("Warning: Could not close %s", g_vol->dev->d_name); + ntfs_device_free(g_vol->dev); + } + free(g_vol->vol_name); + free(g_vol->attrdef); + free(g_vol->upcase); + free(g_vol); + g_vol = NULL; + } + + /* Free any memory we've used */ + free(g_bad_blocks); g_bad_blocks = NULL; + free(g_buf); g_buf = NULL; + free(g_index_block); g_index_block = NULL; + free(g_dynamic_buf); g_dynamic_buf = NULL; + free(g_mft_bitmap); g_mft_bitmap = NULL; + free(g_rl_bad); g_rl_bad = NULL; + free(g_rl_boot); g_rl_boot = NULL; + free(g_rl_logfile); g_rl_logfile = NULL; + free(g_rl_mft); g_rl_mft = NULL; + free(g_rl_mft_bmp); g_rl_mft_bmp = NULL; + free(g_rl_mftmirr); g_rl_mftmirr = NULL; + + p = g_allocation; + while (p) { + q = p->next; + free(p); + p = q; + } +} + + +/** + * mkntfs_open_partition - + */ +static BOOL mkntfs_open_partition(ntfs_volume *vol) +{ + BOOL result = FALSE; + int i; + struct stat sbuf; + unsigned long mnt_flags; + + /* + * Allocate and initialize an ntfs device structure and attach it to + * the volume. + */ + vol->dev = ntfs_device_alloc(opts.dev_name, 0, &ntfs_device_default_io_ops, NULL); + if (!vol->dev) { + ntfs_log_perror("Could not create device"); + goto done; + } + + /* Open the device for reading or reading and writing. */ + if (opts.no_action) { + ntfs_log_quiet("Running in READ-ONLY mode!\n"); + i = O_RDONLY; + } else { + i = O_RDWR; + } + if (vol->dev->d_ops->open(vol->dev, i)) { + if (errno == ENOENT) + ntfs_log_error("The device doesn't exist; did you specify it correctly?\n"); + else + ntfs_log_perror("Could not open %s", vol->dev->d_name); + goto done; + } + /* Verify we are dealing with a block device. */ + if (vol->dev->d_ops->stat(vol->dev, &sbuf)) { + ntfs_log_perror("Error getting information about %s", vol->dev->d_name); + goto done; + } + + if (!S_ISBLK(sbuf.st_mode)) { + ntfs_log_error("%s is not a block device.\n", vol->dev->d_name); + if (!opts.force) { + ntfs_log_error("Refusing to make a filesystem here!\n"); + goto done; + } + if (!opts.num_sectors) { + if (!sbuf.st_size && !sbuf.st_blocks) { + ntfs_log_error("You must specify the number of sectors.\n"); + goto done; + } + if (opts.sector_size) { + if (sbuf.st_size) + opts.num_sectors = sbuf.st_size / opts.sector_size; + else + opts.num_sectors = ((s64)sbuf.st_blocks << 9) / opts.sector_size; + } else { + if (sbuf.st_size) + opts.num_sectors = sbuf.st_size / 512; + else + opts.num_sectors = sbuf.st_blocks; + opts.sector_size = 512; + } + } + ntfs_log_warning("mkntfs forced anyway.\n"); +#ifdef HAVE_LINUX_MAJOR_H + } else if ((IDE_DISK_MAJOR(MAJOR(sbuf.st_rdev)) && + MINOR(sbuf.st_rdev) % 64 == 0) || + (SCSI_DISK_MAJOR(MAJOR(sbuf.st_rdev)) && + MINOR(sbuf.st_rdev) % 16 == 0)) { + ntfs_log_error("%s is entire device, not just one partition.\n", vol->dev->d_name); + if (!opts.force) { + ntfs_log_error("Refusing to make a filesystem here!\n"); + goto done; + } + ntfs_log_warning("mkntfs forced anyway.\n"); +#endif + } + /* Make sure the file system is not mounted. */ + if (ntfs_check_if_mounted(vol->dev->d_name, &mnt_flags)) { + ntfs_log_perror("Failed to determine whether %s is mounted", vol->dev->d_name); + } else if (mnt_flags & NTFS_MF_MOUNTED) { + ntfs_log_error("%s is mounted.\n", vol->dev->d_name); + if (!opts.force) { + ntfs_log_error("Refusing to make a filesystem here!\n"); + goto done; + } + ntfs_log_warning("mkntfs forced anyway. Hope /etc/mtab is incorrect.\n"); + } + result = TRUE; +done: + return result; +} + +/** + * mkntfs_get_page_size - detect the system's memory page size. + */ +static long mkntfs_get_page_size(void) +{ + long page_size; +#ifdef _SC_PAGESIZE + page_size = sysconf(_SC_PAGESIZE); + if (page_size < 0) +#endif + { + ntfs_log_warning("Failed to determine system page size. " + "Assuming safe default of 4096 bytes.\n"); + return 4096; + } + ntfs_log_debug("System page size is %li bytes.\n", page_size); + return page_size; +} + +/** + * mkntfs_override_vol_params - + */ +static BOOL mkntfs_override_vol_params(ntfs_volume *vol) +{ + s64 volume_size; + long page_size; + int i; + BOOL winboot = TRUE; + + /* If user didn't specify the sector size, determine it now. */ + if (opts.sector_size < 0) { + opts.sector_size = ntfs_device_sector_size_get(vol->dev); + if (opts.sector_size < 0) { + ntfs_log_warning("The sector size was not specified " + "for %s and it could not be obtained " + "automatically. It has been set to 512 " + "bytes.\n", vol->dev->d_name); + opts.sector_size = 512; + } + } + /* Validate sector size. */ + if ((opts.sector_size - 1) & opts.sector_size) { + ntfs_log_error("The sector size is invalid. It must be a " + "power of two, e.g. 512, 1024.\n"); + return FALSE; + } + if (opts.sector_size < 256 || opts.sector_size > 4096) { + ntfs_log_error("The sector size is invalid. The minimum size " + "is 256 bytes and the maximum is 4096 bytes.\n"); + return FALSE; + } + ntfs_log_debug("sector size = %ld bytes\n", opts.sector_size); + /* Now set the device block size to the sector size. */ + if (ntfs_device_block_size_set(vol->dev, opts.sector_size)) + ntfs_log_debug("Failed to set the device block size to the " + "sector size. This may cause problems when " + "creating the backup boot sector and also may " + "affect performance but should be harmless " + "otherwise. Error: %s\n", strerror(errno)); + /* If user didn't specify the number of sectors, determine it now. */ + if (opts.num_sectors < 0) { + opts.num_sectors = ntfs_device_size_get(vol->dev, + opts.sector_size); + if (opts.num_sectors <= 0) { + ntfs_log_error("Couldn't determine the size of %s. " + "Please specify the number of sectors " + "manually.\n", vol->dev->d_name); + return FALSE; + } + } + ntfs_log_debug("number of sectors = %lld (0x%llx)\n", opts.num_sectors, + opts.num_sectors); + /* + * Reserve the last sector for the backup boot sector unless the + * sector size is less than 512 bytes in which case reserve 512 bytes + * worth of sectors. + */ + i = 1; + if (opts.sector_size < 512) + i = 512 / opts.sector_size; + opts.num_sectors -= i; + /* If user didn't specify the partition start sector, determine it. */ + if (opts.part_start_sect < 0) { + opts.part_start_sect = ntfs_device_partition_start_sector_get( + vol->dev); + if (opts.part_start_sect < 0) { + ntfs_log_warning("The partition start sector was not " + "specified for %s and it could not be obtained " + "automatically. It has been set to 0.\n", + vol->dev->d_name); + opts.part_start_sect = 0; + winboot = FALSE; + } else if (opts.part_start_sect >> 32) { + ntfs_log_warning("The partition start sector specified " + "for %s and the automatically determined value " + "is too large. It has been set to 0.\n", + vol->dev->d_name); + opts.part_start_sect = 0; + winboot = FALSE; + } + } else if (opts.part_start_sect >> 32) { + ntfs_log_error("Invalid partition start sector. Maximum is " + "4294967295 (2^32-1).\n"); + return FALSE; + } + /* If user didn't specify the sectors per track, determine it now. */ + if (opts.sectors_per_track < 0) { + opts.sectors_per_track = ntfs_device_sectors_per_track_get( + vol->dev); + if (opts.sectors_per_track < 0) { + ntfs_log_warning("The number of sectors per track was " + "not specified for %s and it could not be " + "obtained automatically. It has been set to " + "0.\n", vol->dev->d_name); + opts.sectors_per_track = 0; + winboot = FALSE; + } else if (opts.sectors_per_track > 65535) { + ntfs_log_warning("The number of sectors per track was " + "not specified for %s and the automatically " + "determined value is too large. It has been " + "set to 0.\n", vol->dev->d_name); + opts.sectors_per_track = 0; + winboot = FALSE; + } + } else if (opts.sectors_per_track > 65535) { + ntfs_log_error("Invalid number of sectors per track. Maximum " + "is 65535.\n"); + return FALSE; + } + /* If user didn't specify the number of heads, determine it now. */ + if (opts.heads < 0) { + opts.heads = ntfs_device_heads_get(vol->dev); + if (opts.heads < 0) { + ntfs_log_warning("The number of heads was not " + "specified for %s and it could not be obtained " + "automatically. It has been set to 0.\n", + vol->dev->d_name); + opts.heads = 0; + winboot = FALSE; + } else if (opts.heads > 65535) { + ntfs_log_warning("The number of heads was not " + "specified for %s and the automatically " + "determined value is too large. It has been " + "set to 0.\n", vol->dev->d_name); + opts.heads = 0; + winboot = FALSE; + } + } else if (opts.heads > 65535) { + ntfs_log_error("Invalid number of heads. Maximum is 65535.\n"); + return FALSE; + } + volume_size = opts.num_sectors * opts.sector_size; + /* Validate volume size. */ + if (volume_size < (1 << 20)) { /* 1MiB */ + ntfs_log_error("Device is too small (%llikiB). Minimum NTFS " + "volume size is 1MiB.\n", + (long long)(volume_size / 1024)); + return FALSE; + } + ntfs_log_debug("volume size = %llikiB\n", volume_size / 1024); + /* If user didn't specify the cluster size, determine it now. */ + if (!vol->cluster_size) { + /* + * Windows Vista always uses 4096 bytes as the default cluster + * size regardless of the volume size so we do it, too. + */ + vol->cluster_size = 4096; + /* For small volumes on devices with large sector sizes. */ + if (vol->cluster_size < (u32)opts.sector_size) + vol->cluster_size = opts.sector_size; + /* + * For huge volumes, grow the cluster size until the number of + * clusters fits into 32 bits or the cluster size exceeds the + * maximum limit of 64kiB. + */ + while (volume_size >> (ffs(vol->cluster_size) - 1 + 32)) { + vol->cluster_size <<= 1; + if (vol->cluster_size > 65535) { + ntfs_log_error("Device is too large to hold an " + "NTFS volume (maximum size is " + "256TiB).\n"); + return FALSE; + } + } + ntfs_log_quiet("Cluster size has been automatically set to %u " + "bytes.\n", (unsigned)vol->cluster_size); + } + /* Validate cluster size. */ + if (vol->cluster_size & (vol->cluster_size - 1)) { + ntfs_log_error("The cluster size is invalid. It must be a " + "power of two, e.g. 1024, 4096.\n"); + return FALSE; + } + if (vol->cluster_size < (u32)opts.sector_size) { + ntfs_log_error("The cluster size is invalid. It must be equal " + "to, or larger than, the sector size.\n"); + return FALSE; + } + if (vol->cluster_size > 128 * (u32)opts.sector_size) { + ntfs_log_error("The cluster size is invalid. It cannot be " + "more that 128 times the size of the sector " + "size.\n"); + return FALSE; + } + if (vol->cluster_size > 65536) { + ntfs_log_error("The cluster size is invalid. The maximum " + "cluster size is 65536 bytes (64kiB).\n"); + return FALSE; + } + vol->cluster_size_bits = ffs(vol->cluster_size) - 1; + ntfs_log_debug("cluster size = %u bytes\n", + (unsigned int)vol->cluster_size); + if (vol->cluster_size > 4096) { + if (opts.enable_compression) { + if (!opts.force) { + ntfs_log_error("Windows cannot use compression " + "when the cluster size is " + "larger than 4096 bytes.\n"); + return FALSE; + } + opts.enable_compression = 0; + } + ntfs_log_warning("Windows cannot use compression when the " + "cluster size is larger than 4096 bytes. " + "Compression has been disabled for this " + "volume.\n"); + } + vol->nr_clusters = volume_size / vol->cluster_size; + /* + * Check the cluster_size and num_sectors for consistency with + * sector_size and num_sectors. And check both of these for consistency + * with volume_size. + */ + if ((vol->nr_clusters != ((opts.num_sectors * opts.sector_size) / + vol->cluster_size) || + (volume_size / opts.sector_size) != opts.num_sectors || + (volume_size / vol->cluster_size) != + vol->nr_clusters)) { + /* XXX is this code reachable? */ + ntfs_log_error("Illegal combination of volume/cluster/sector " + "size and/or cluster/sector number.\n"); + return FALSE; + } + ntfs_log_debug("number of clusters = %llu (0x%llx)\n", + vol->nr_clusters, vol->nr_clusters); + /* Number of clusters must fit within 32 bits (Win2k limitation). */ + if (vol->nr_clusters >> 32) { + if (vol->cluster_size >= 65536) { + ntfs_log_error("Device is too large to hold an NTFS " + "volume (maximum size is 256TiB).\n"); + return FALSE; + } + ntfs_log_error("Number of clusters exceeds 32 bits. Please " + "try again with a larger\ncluster size or " + "leave the cluster size unspecified and the " + "smallest possible cluster size for the size " + "of the device will be used.\n"); + return FALSE; + } + page_size = mkntfs_get_page_size(); + /* + * Set the mft record size. By default this is 1024 but it has to be + * at least as big as a sector and not bigger than a page on the system + * or the NTFS kernel driver will not be able to mount the volume. + * TODO: The mft record size should be user specifiable just like the + * "inode size" can be specified on other Linux/Unix file systems. + */ + vol->mft_record_size = 1024; + if (vol->mft_record_size < (u32)opts.sector_size) + vol->mft_record_size = opts.sector_size; + if (vol->mft_record_size > (unsigned long)page_size) + ntfs_log_warning("Mft record size (%u bytes) exceeds system " + "page size (%li bytes). You will not be able " + "to mount this volume using the NTFS kernel " + "driver.\n", (unsigned)vol->mft_record_size, + page_size); + vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; + ntfs_log_debug("mft record size = %u bytes\n", + (unsigned)vol->mft_record_size); + /* + * Set the index record size. By default this is 4096 but it has to be + * at least as big as a sector and not bigger than a page on the system + * or the NTFS kernel driver will not be able to mount the volume. + * FIXME: Should we make the index record size to be user specifiable? + */ + vol->indx_record_size = 4096; + if (vol->indx_record_size < (u32)opts.sector_size) + vol->indx_record_size = opts.sector_size; + if (vol->indx_record_size > (unsigned long)page_size) + ntfs_log_warning("Index record size (%u bytes) exceeds system " + "page size (%li bytes). You will not be able " + "to mount this volume using the NTFS kernel " + "driver.\n", (unsigned)vol->indx_record_size, + page_size); + vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1; + ntfs_log_debug("index record size = %u bytes\n", + (unsigned)vol->indx_record_size); + if (!winboot) { + ntfs_log_warning("To boot from a device, Windows needs the " + "'partition start sector', the 'sectors per " + "track' and the 'number of heads' to be " + "set.\n"); + ntfs_log_warning("Windows will not be able to boot from this " + "device.\n"); + } + return TRUE; +} + +/** + * mkntfs_initialize_bitmaps - + */ +static BOOL mkntfs_initialize_bitmaps(void) +{ + u64 i; + int mft_bitmap_size; + + /* Determine lcn bitmap byte size and allocate it. */ + g_lcn_bitmap_byte_size = (g_vol->nr_clusters + 7) >> 3; + /* Needs to be multiple of 8 bytes. */ + g_lcn_bitmap_byte_size = (g_lcn_bitmap_byte_size + 7) & ~7; + i = (g_lcn_bitmap_byte_size + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1); + ntfs_log_debug("g_lcn_bitmap_byte_size = %i, allocated = %llu\n", + g_lcn_bitmap_byte_size, i); + g_dynamic_buf_size = mkntfs_get_page_size(); + g_dynamic_buf = (u8*)ntfs_calloc(g_dynamic_buf_size); + if (!g_dynamic_buf) + return FALSE; + /* + * $Bitmap can overlap the end of the volume. Any bits in this region + * must be set. This region also encompasses the backup boot sector. + */ + if (!bitmap_allocate(g_vol->nr_clusters, + ((s64)g_lcn_bitmap_byte_size << 3) - g_vol->nr_clusters)) + return (FALSE); + /* + * Mft size is 27 (NTFS 3.0+) mft records or one cluster, whichever is + * bigger. + */ + g_mft_size = 27; + g_mft_size *= g_vol->mft_record_size; + if (g_mft_size < (s32)g_vol->cluster_size) + g_mft_size = g_vol->cluster_size; + ntfs_log_debug("MFT size = %i (0x%x) bytes\n", g_mft_size, g_mft_size); + /* Determine mft bitmap size and allocate it. */ + mft_bitmap_size = g_mft_size / g_vol->mft_record_size; + /* Convert to bytes, at least one. */ + g_mft_bitmap_byte_size = (mft_bitmap_size + 7) >> 3; + /* Mft bitmap is allocated in multiples of 8 bytes. */ + g_mft_bitmap_byte_size = (g_mft_bitmap_byte_size + 7) & ~7; + ntfs_log_debug("mft_bitmap_size = %i, g_mft_bitmap_byte_size = %i\n", + mft_bitmap_size, g_mft_bitmap_byte_size); + g_mft_bitmap = ntfs_calloc(g_mft_bitmap_byte_size); + if (!g_mft_bitmap) + return FALSE; + /* Create runlist for mft bitmap. */ + g_rl_mft_bmp = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_mft_bmp) + return FALSE; + + g_rl_mft_bmp[0].vcn = 0LL; + /* Mft bitmap is right after $Boot's data. */ + i = (8192 + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_mft_bmp[0].lcn = i; + /* + * Size is always one cluster, even though valid data size and + * initialized data size are only 8 bytes. + */ + g_rl_mft_bmp[1].vcn = 1LL; + g_rl_mft_bmp[0].length = 1LL; + g_rl_mft_bmp[1].lcn = -1LL; + g_rl_mft_bmp[1].length = 0LL; + /* Allocate cluster for mft bitmap. */ + return (bitmap_allocate(i,1)); +} + +/** + * mkntfs_initialize_rl_mft - + */ +static BOOL mkntfs_initialize_rl_mft(void) +{ + int j; + BOOL done; + + /* If user didn't specify the mft lcn, determine it now. */ + if (!g_mft_lcn) { + /* + * We start at the higher value out of 16kiB and just after the + * mft bitmap. + */ + g_mft_lcn = g_rl_mft_bmp[0].lcn + g_rl_mft_bmp[0].length; + if (g_mft_lcn * g_vol->cluster_size < 16 * 1024) + g_mft_lcn = (16 * 1024 + g_vol->cluster_size - 1) / + g_vol->cluster_size; + } + ntfs_log_debug("$MFT logical cluster number = 0x%llx\n", g_mft_lcn); + /* Determine MFT zone size. */ + g_mft_zone_end = g_vol->nr_clusters; + switch (opts.mft_zone_multiplier) { /* % of volume size in clusters */ + case 4: + g_mft_zone_end = g_mft_zone_end >> 1; /* 50% */ + break; + case 3: + g_mft_zone_end = g_mft_zone_end * 3 >> 3;/* 37.5% */ + break; + case 2: + g_mft_zone_end = g_mft_zone_end >> 2; /* 25% */ + break; + case 1: + default: + g_mft_zone_end = g_mft_zone_end >> 3; /* 12.5% */ + break; + } + ntfs_log_debug("MFT zone size = %lldkiB\n", g_mft_zone_end << + g_vol->cluster_size_bits >> 10 /* >> 10 == / 1024 */); + /* + * The mft zone begins with the mft data attribute, not at the beginning + * of the device. + */ + g_mft_zone_end += g_mft_lcn; + /* Create runlist for mft. */ + g_rl_mft = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_mft) + return FALSE; + + g_rl_mft[0].vcn = 0LL; + g_rl_mft[0].lcn = g_mft_lcn; + /* rounded up division by cluster size */ + j = (g_mft_size + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_mft[1].vcn = j; + g_rl_mft[0].length = j; + g_rl_mft[1].lcn = -1LL; + g_rl_mft[1].length = 0LL; + /* Allocate clusters for mft. */ + bitmap_allocate(g_mft_lcn,j); + /* Determine mftmirr_lcn (middle of volume). */ + g_mftmirr_lcn = (opts.num_sectors * opts.sector_size >> 1) + / g_vol->cluster_size; + ntfs_log_debug("$MFTMirr logical cluster number = 0x%llx\n", + g_mftmirr_lcn); + /* Create runlist for mft mirror. */ + g_rl_mftmirr = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_mftmirr) + return FALSE; + + g_rl_mftmirr[0].vcn = 0LL; + g_rl_mftmirr[0].lcn = g_mftmirr_lcn; + /* + * The mft mirror is either 4kb (the first four records) or one cluster + * in size, which ever is bigger. In either case, it contains a + * byte-for-byte identical copy of the beginning of the mft (i.e. either + * the first four records (4kb) or the first cluster worth of records, + * whichever is bigger). + */ + j = (4 * g_vol->mft_record_size + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_mftmirr[1].vcn = j; + g_rl_mftmirr[0].length = j; + g_rl_mftmirr[1].lcn = -1LL; + g_rl_mftmirr[1].length = 0LL; + /* Allocate clusters for mft mirror. */ + done = bitmap_allocate(g_mftmirr_lcn,j); + g_logfile_lcn = g_mftmirr_lcn + j; + ntfs_log_debug("$LogFile logical cluster number = 0x%llx\n", + g_logfile_lcn); + return (done); +} + +/** + * mkntfs_initialize_rl_logfile - + */ +static BOOL mkntfs_initialize_rl_logfile(void) +{ + int j; + u64 volume_size; + + /* Create runlist for log file. */ + g_rl_logfile = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_logfile) + return FALSE; + + + volume_size = g_vol->nr_clusters << g_vol->cluster_size_bits; + + g_rl_logfile[0].vcn = 0LL; + g_rl_logfile[0].lcn = g_logfile_lcn; + /* + * Determine logfile_size from volume_size (rounded up to a cluster), + * making sure it does not overflow the end of the volume. + */ + if (volume_size < 2048LL * 1024) /* < 2MiB */ + g_logfile_size = 256LL * 1024; /* -> 256kiB */ + else if (volume_size < 4000000LL) /* < 4MB */ + g_logfile_size = 512LL * 1024; /* -> 512kiB */ + else if (volume_size <= 200LL * 1024 * 1024) /* < 200MiB */ + g_logfile_size = 2048LL * 1024; /* -> 2MiB */ + else { + /* + * FIXME: The $LogFile size is 64 MiB upwards from 12GiB but + * the "200" divider below apparently approximates "100" or + * some other value as the volume size decreases. For example: + * Volume size LogFile size Ratio + * 8799808 46048 191.100 + * 8603248 45072 190.877 + * 7341704 38768 189.375 + * 6144828 32784 187.433 + * 4192932 23024 182.111 + */ + if (volume_size >= 12LL << 30) /* > 12GiB */ + g_logfile_size = 64 << 20; /* -> 64MiB */ + else + g_logfile_size = (volume_size / 200) & + ~(g_vol->cluster_size - 1); + } + j = g_logfile_size / g_vol->cluster_size; + while (g_rl_logfile[0].lcn + j >= g_vol->nr_clusters) { + /* + * $Logfile would overflow volume. Need to make it smaller than + * the standard size. It's ok as we are creating a non-standard + * volume anyway if it is that small. + */ + g_logfile_size >>= 1; + j = g_logfile_size / g_vol->cluster_size; + } + g_logfile_size = (g_logfile_size + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1); + ntfs_log_debug("$LogFile (journal) size = %ikiB\n", + g_logfile_size / 1024); + /* + * FIXME: The 256kiB limit is arbitrary. Should find out what the real + * minimum requirement for Windows is so it doesn't blue screen. + */ + if (g_logfile_size < 256 << 10) { + ntfs_log_error("$LogFile would be created with invalid size. " + "This is not allowed as it would cause Windows " + "to blue screen and during boot.\n"); + return FALSE; + } + g_rl_logfile[1].vcn = j; + g_rl_logfile[0].length = j; + g_rl_logfile[1].lcn = -1LL; + g_rl_logfile[1].length = 0LL; + /* Allocate clusters for log file. */ + return (bitmap_allocate(g_logfile_lcn,j)); +} + +/** + * mkntfs_initialize_rl_boot - + */ +static BOOL mkntfs_initialize_rl_boot(void) +{ + int j; + /* Create runlist for $Boot. */ + g_rl_boot = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_boot) + return FALSE; + + g_rl_boot[0].vcn = 0LL; + g_rl_boot[0].lcn = 0LL; + /* + * $Boot is always 8192 (0x2000) bytes or 1 cluster, whichever is + * bigger. + */ + j = (8192 + g_vol->cluster_size - 1) / g_vol->cluster_size; + g_rl_boot[1].vcn = j; + g_rl_boot[0].length = j; + g_rl_boot[1].lcn = -1LL; + g_rl_boot[1].length = 0LL; + /* Allocate clusters for $Boot. */ + return (bitmap_allocate(0,j)); +} + +/** + * mkntfs_initialize_rl_bad - + */ +static BOOL mkntfs_initialize_rl_bad(void) +{ + /* Create runlist for $BadClus, $DATA named stream $Bad. */ + g_rl_bad = ntfs_malloc(2 * sizeof(runlist)); + if (!g_rl_bad) + return FALSE; + + g_rl_bad[0].vcn = 0LL; + g_rl_bad[0].lcn = -1LL; + /* + * $BadClus named stream $Bad contains the whole volume as a single + * sparse runlist entry. + */ + g_rl_bad[1].vcn = g_vol->nr_clusters; + g_rl_bad[0].length = g_vol->nr_clusters; + g_rl_bad[1].lcn = -1LL; + g_rl_bad[1].length = 0LL; + + /* TODO: Mark bad blocks as such. */ + return TRUE; +} + +/** + * mkntfs_fill_device_with_zeroes - + */ +static BOOL mkntfs_fill_device_with_zeroes(void) +{ + /* + * If not quick format, fill the device with 0s. + * FIXME: Except bad blocks! (AIA) + */ + int i; + ssize_t bw; + unsigned long long position; + float progress_inc = (float)g_vol->nr_clusters / 100; + u64 volume_size; + + volume_size = g_vol->nr_clusters << g_vol->cluster_size_bits; + + ntfs_log_progress("Initializing device with zeroes: 0%%"); + for (position = 0; position < (unsigned long long)g_vol->nr_clusters; + position++) { + if (!(position % (int)(progress_inc+1))) { + ntfs_log_progress("\b\b\b\b%3.0f%%", position / + progress_inc); + } + bw = mkntfs_write(g_vol->dev, g_buf, g_vol->cluster_size); + if (bw != (ssize_t)g_vol->cluster_size) { + if (bw != -1 || errno != EIO) { + ntfs_log_error("This should not happen.\n"); + return FALSE; + } + if (!position) { + ntfs_log_error("Error: Cluster zero is bad. " + "Cannot create NTFS file " + "system.\n"); + return FALSE; + } + /* Add the baddie to our bad blocks list. */ + if (!append_to_bad_blocks(position)) + return FALSE; + ntfs_log_quiet("\nFound bad cluster (%lld). Adding to " + "list of bad blocks.\nInitializing " + "device with zeroes: %3.0f%%", position, + position / progress_inc); + /* Seek to next cluster. */ + g_vol->dev->d_ops->seek(g_vol->dev, + ((off_t)position + 1) * + g_vol->cluster_size, SEEK_SET); + } + } + ntfs_log_progress("\b\b\b\b100%%"); + position = (volume_size & (g_vol->cluster_size - 1)) / + opts.sector_size; + for (i = 0; (unsigned long)i < position; i++) { + bw = mkntfs_write(g_vol->dev, g_buf, opts.sector_size); + if (bw != opts.sector_size) { + if (bw != -1 || errno != EIO) { + ntfs_log_error("This should not happen.\n"); + return FALSE; + } else if (i + 1ull == position) { + ntfs_log_error("Error: Bad cluster found in " + "location reserved for system " + "file $Boot.\n"); + return FALSE; + } + /* Seek to next sector. */ + g_vol->dev->d_ops->seek(g_vol->dev, + opts.sector_size, SEEK_CUR); + } + } + ntfs_log_progress(" - Done.\n"); + return TRUE; +} + +/** + * mkntfs_sync_index_record + * + * (ERSO) made a function out of this, but the reason for doing that + * disappeared during coding.... + */ +static BOOL mkntfs_sync_index_record(INDEX_ALLOCATION* idx, MFT_RECORD* m, + ntfschar* name, u32 name_len) +{ + int i, err; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + long long lw; + runlist *rl_index = NULL; + + i = 5 * sizeof(ntfschar); + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_perror("Failed to allocate attribute search context"); + return FALSE; + } + /* FIXME: This should be IGNORE_CASE! */ + if (mkntfs_attr_lookup(AT_INDEX_ALLOCATION, name, name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_attr_put_search_ctx(ctx); + ntfs_log_error("BUG: $INDEX_ALLOCATION attribute not found.\n"); + return FALSE; + } + a = ctx->attr; + rl_index = ntfs_mapping_pairs_decompress(g_vol, a, NULL); + if (!rl_index) { + ntfs_attr_put_search_ctx(ctx); + ntfs_log_error("Failed to decompress runlist of $INDEX_ALLOCATION " + "attribute.\n"); + return FALSE; + } + if (sle64_to_cpu(a->initialized_size) < i) { + ntfs_attr_put_search_ctx(ctx); + free(rl_index); + ntfs_log_error("BUG: $INDEX_ALLOCATION attribute too short.\n"); + return FALSE; + } + ntfs_attr_put_search_ctx(ctx); + i = sizeof(INDEX_BLOCK) - sizeof(INDEX_HEADER) + + le32_to_cpu(idx->index.allocated_size); + err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)idx, i); + if (err) { + free(rl_index); + ntfs_log_error("ntfs_mst_pre_write_fixup() failed while " + "syncing index block.\n"); + return FALSE; + } + lw = ntfs_rlwrite(g_vol->dev, rl_index, (u8*)idx, i, NULL, + WRITE_STANDARD); + free(rl_index); + if (lw != i) { + ntfs_log_error("Error writing $INDEX_ALLOCATION.\n"); + return FALSE; + } + /* No more changes to @idx below here so no need for fixup: */ + /* ntfs_mst_post_write_fixup((NTFS_RECORD*)idx); */ + return TRUE; +} + +/** + * create_file_volume - + */ +static BOOL create_file_volume(MFT_RECORD *m, leMFT_REF root_ref, + VOLUME_FLAGS fl, const GUID *volume_guid) +{ + int i, err; + u8 *sd; + + ntfs_log_verbose("Creating $Volume (mft record 3)\n"); + m = (MFT_RECORD*)(g_buf + 3 * g_vol->mft_record_size); + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_Volume, FILE_Volume), 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$Volume", FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_system_file_sd(FILE_Volume, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (!err) + err = add_attr_data(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), NULL, 0); + if (!err) + err = add_attr_vol_name(m, g_vol->vol_name, g_vol->vol_name ? + strlen(g_vol->vol_name) : 0); + if (!err) { + if (fl & VOLUME_IS_DIRTY) + ntfs_log_quiet("Setting the volume dirty so check " + "disk runs on next reboot into " + "Windows.\n"); + err = add_attr_vol_info(m, fl, g_vol->major_ver, + g_vol->minor_ver); + } + if (!err && opts.with_uuid) + err = add_attr_object_id(m, volume_guid); + if (err < 0) { + ntfs_log_error("Couldn't create $Volume: %s\n", + strerror(-err)); + return FALSE; + } + return TRUE; +} + +/** + * create_backup_boot_sector + * + * Return 0 on success or -1 if it couldn't be created. + */ +static int create_backup_boot_sector(u8 *buff) +{ + const char *s; + ssize_t bw; + int size, e; + + ntfs_log_verbose("Creating backup boot sector.\n"); + /* + * Write the first max(512, opts.sector_size) bytes from buf to the + * last sector, but limit that to 8192 bytes of written data since that + * is how big $Boot is (and how big our buffer is).. + */ + size = 512; + if (size < opts.sector_size) + size = opts.sector_size; + if (g_vol->dev->d_ops->seek(g_vol->dev, (opts.num_sectors + 1) * + opts.sector_size - size, SEEK_SET) == (off_t)-1) { + ntfs_log_perror("Seek failed"); + goto bb_err; + } + if (size > 8192) + size = 8192; + bw = mkntfs_write(g_vol->dev, buff, size); + if (bw == size) + return 0; + e = errno; + if (bw == -1LL) + s = strerror(e); + else + s = "unknown error"; + /* At least some 2.4 kernels return EIO instead of ENOSPC. */ + if (bw != -1LL || (bw == -1LL && e != ENOSPC && e != EIO)) { + ntfs_log_critical("Couldn't write backup boot sector: %s\n", s); + return -1; + } +bb_err: + ntfs_log_error("Couldn't write backup boot sector. This is due to a " + "limitation in the\nLinux kernel. This is not a major " + "problem as Windows check disk will create the\n" + "backup boot sector when it is run on your next boot " + "into Windows.\n"); + return -1; +} + +/** + * mkntfs_create_root_structures - + */ +static BOOL mkntfs_create_root_structures(void) +{ + NTFS_BOOT_SECTOR *bs; + MFT_RECORD *m; + leMFT_REF root_ref; + leMFT_REF extend_ref; + int i; + int j; + int err; + u8 *sd; + FILE_ATTR_FLAGS extend_flags; + VOLUME_FLAGS volume_flags = const_cpu_to_le16(0); + int nr_sysfiles; + int buf_sds_first_size; + char *buf_sds; + GUID vol_guid; + + ntfs_log_quiet("Creating NTFS volume structures.\n"); + nr_sysfiles = 27; + /* + * Setup an empty mft record. Note, we can just give 0 as the mft + * reference as we are creating an NTFS 1.2 volume for which the mft + * reference is ignored by ntfs_mft_record_layout(). + * + * Copy the mft record onto all 16 records in the buffer and setup the + * sequence numbers of each system file to equal the mft record number + * of that file (only for $MFT is the sequence number 1 rather than 0). + */ + for (i = 0; i < nr_sysfiles; i++) { + if (ntfs_mft_record_layout(g_vol, 0, m = (MFT_RECORD *)(g_buf + + i * g_vol->mft_record_size))) { + ntfs_log_error("Failed to layout system mft records." + "\n"); + return FALSE; + } + if (i == 0 || i > 23) + m->sequence_number = cpu_to_le16(1); + else + m->sequence_number = cpu_to_le16(i); + } + /* + * If only one cluster contains all system files then + * fill the rest of it with empty, formatted records. + */ + if (nr_sysfiles * (s32)g_vol->mft_record_size < g_mft_size) { + for (i = nr_sysfiles; + i * (s32)g_vol->mft_record_size < g_mft_size; i++) { + m = (MFT_RECORD *)(g_buf + i * g_vol->mft_record_size); + if (ntfs_mft_record_layout(g_vol, 0, m)) { + ntfs_log_error("Failed to layout mft record." + "\n"); + return FALSE; + } + m->flags = cpu_to_le16(0); + m->sequence_number = cpu_to_le16(i); + } + } + /* + * Create the 16 system files, adding the system information attribute + * to each as well as marking them in use in the mft bitmap. + */ + for (i = 0; i < nr_sysfiles; i++) { + le32 file_attrs; + + m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); + if (i < 16 || i > 23) { + m->mft_record_number = cpu_to_le32(i); + m->flags |= MFT_RECORD_IN_USE; + ntfs_bit_set(g_mft_bitmap, 0LL + i, 1); + } + file_attrs = FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM; + if (i == FILE_root) { + file_attrs |= FILE_ATTR_ARCHIVE; + if (opts.disable_indexing) + file_attrs |= FILE_ATTR_NOT_CONTENT_INDEXED; + if (opts.enable_compression) + file_attrs |= FILE_ATTR_COMPRESSED; + } + /* setting specific security_id flag and */ + /* file permissions for ntfs 3.x */ + if (i == 0 || i == 1 || i == 2 || i == 6 || i == 8 || + i == 10) { + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0100)); + } else if (i == 9) { + file_attrs |= FILE_ATTR_VIEW_INDEX_PRESENT; + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0101)); + } else if (i == 11) { + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0101)); + } else if (i == 24 || i == 25 || i == 26) { + file_attrs |= FILE_ATTR_ARCHIVE; + file_attrs |= FILE_ATTR_VIEW_INDEX_PRESENT; + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x0101)); + } else { + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x00)); + } + } + /* The root directory mft reference. */ + root_ref = MK_LE_MREF(FILE_root, FILE_root); + extend_ref = MK_LE_MREF(11,11); + ntfs_log_verbose("Creating root directory (mft record 5)\n"); + m = (MFT_RECORD*)(g_buf + 5 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_DIRECTORY; + m->link_count = cpu_to_le16(le16_to_cpu(m->link_count) + 1); + err = add_attr_file_name(m, root_ref, 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_I30_INDEX_PRESENT, 0, 0, ".", + FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_root_sd(&sd, &i); + err = add_attr_sd(m, sd, i); + } + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$I30", 4, CASE_SENSITIVE, + AT_FILE_NAME, COLLATION_FILE_NAME, + g_vol->indx_record_size); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = upgrade_to_large_index(m, "$I30", 4, CASE_SENSITIVE, + &g_index_block); + if (!err) { + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_perror("Failed to allocate attribute search " + "context"); + return FALSE; + } + /* There is exactly one file name so this is ok. */ + if (mkntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_attr_put_search_ctx(ctx); + ntfs_log_error("BUG: $FILE_NAME attribute not found." + "\n"); + return FALSE; + } + a = ctx->attr; + err = insert_file_link_in_dir_index(g_index_block, root_ref, + (FILE_NAME_ATTR*)((char*)a + + le16_to_cpu(a->value_offset)), + le32_to_cpu(a->value_length)); + ntfs_attr_put_search_ctx(ctx); + } + if (err) { + ntfs_log_error("Couldn't create root directory: %s\n", + strerror(-err)); + return FALSE; + } + /* Add all other attributes, on a per-file basis for clarity. */ + ntfs_log_verbose("Creating $MFT (mft record 0)\n"); + m = (MFT_RECORD*)g_buf; + err = add_attr_data_positioned(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), g_rl_mft, g_buf, g_mft_size); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_MFT, 1), + ((g_mft_size - 1) + | (g_vol->cluster_size - 1)) + 1, + g_mft_size, FILE_ATTR_HIDDEN | + FILE_ATTR_SYSTEM, 0, 0, "$MFT", + FILE_NAME_WIN32_AND_DOS); + /* mft_bitmap is not modified in mkntfs; no need to sync it later. */ + if (!err) + err = add_attr_bitmap_positioned(m, NULL, 0, CASE_SENSITIVE, + g_rl_mft_bmp, + g_mft_bitmap, g_mft_bitmap_byte_size); + if (err < 0) { + ntfs_log_error("Couldn't create $MFT: %s\n", strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $MFTMirr (mft record 1)\n"); + m = (MFT_RECORD*)(g_buf + 1 * g_vol->mft_record_size); + err = add_attr_data_positioned(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), g_rl_mftmirr, g_buf, + g_rl_mftmirr[0].length * g_vol->cluster_size); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_MFTMirr, FILE_MFTMirr), + g_rl_mftmirr[0].length * g_vol->cluster_size, + g_rl_mftmirr[0].length * g_vol->cluster_size, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$MFTMirr", FILE_NAME_WIN32_AND_DOS); + if (err < 0) { + ntfs_log_error("Couldn't create $MFTMirr: %s\n", + strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $LogFile (mft record 2)\n"); + m = (MFT_RECORD*)(g_buf + 2 * g_vol->mft_record_size); + err = add_attr_data_positioned(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), g_rl_logfile, + (const u8*)NULL, g_logfile_size); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_LogFile, FILE_LogFile), + g_logfile_size, g_logfile_size, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$LogFile", FILE_NAME_WIN32_AND_DOS); + if (err < 0) { + ntfs_log_error("Couldn't create $LogFile: %s\n", + strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $AttrDef (mft record 4)\n"); + m = (MFT_RECORD*)(g_buf + 4 * g_vol->mft_record_size); + err = add_attr_data(m, NULL, 0, CASE_SENSITIVE, const_cpu_to_le16(0), + (u8*)g_vol->attrdef, g_vol->attrdef_len); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_AttrDef, FILE_AttrDef), + (g_vol->attrdef_len + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1), g_vol->attrdef_len, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$AttrDef", FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_system_file_sd(FILE_AttrDef, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + ntfs_log_error("Couldn't create $AttrDef: %s\n", + strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $Bitmap (mft record 6)\n"); + m = (MFT_RECORD*)(g_buf + 6 * g_vol->mft_record_size); + /* the data attribute of $Bitmap must be non-resident or otherwise */ + /* windows 2003 will regard the volume as corrupt (ERSO) */ + if (!err) + err = insert_non_resident_attr_in_mft_record(m, + AT_DATA, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), (const u8*)NULL, + g_lcn_bitmap_byte_size, WRITE_BITMAP); + + + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_Bitmap, FILE_Bitmap), + (g_lcn_bitmap_byte_size + g_vol->cluster_size - + 1) & ~(g_vol->cluster_size - 1), + g_lcn_bitmap_byte_size, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$Bitmap", FILE_NAME_WIN32_AND_DOS); + if (err < 0) { + ntfs_log_error("Couldn't create $Bitmap: %s\n", strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $Boot (mft record 7)\n"); + m = (MFT_RECORD*)(g_buf + 7 * g_vol->mft_record_size); + bs = ntfs_calloc(8192); + if (!bs) + return FALSE; + memcpy(bs, boot_array, sizeof(boot_array)); + /* + * Create the boot sector in bs. Note, that bs is already zeroed + * in the boot sector section and that it has the NTFS OEM id/magic + * already inserted, so no need to worry about these things. + */ + bs->bpb.bytes_per_sector = cpu_to_le16(opts.sector_size); + bs->bpb.sectors_per_cluster = (u8)(g_vol->cluster_size / + opts.sector_size); + bs->bpb.media_type = 0xf8; /* hard disk */ + bs->bpb.sectors_per_track = cpu_to_le16(opts.sectors_per_track); + ntfs_log_debug("sectors per track = %ld (0x%lx)\n", + opts.sectors_per_track, opts.sectors_per_track); + bs->bpb.heads = cpu_to_le16(opts.heads); + ntfs_log_debug("heads = %ld (0x%lx)\n", opts.heads, opts.heads); + bs->bpb.hidden_sectors = cpu_to_le32(opts.part_start_sect); + ntfs_log_debug("hidden sectors = %llu (0x%llx)\n", opts.part_start_sect, + opts.part_start_sect); + bs->physical_drive = 0x80; /* boot from hard disk */ + bs->extended_boot_signature = 0x80; /* everybody sets this, so we do */ + bs->number_of_sectors = cpu_to_sle64(opts.num_sectors); + bs->mft_lcn = cpu_to_sle64(g_mft_lcn); + bs->mftmirr_lcn = cpu_to_sle64(g_mftmirr_lcn); + if (g_vol->mft_record_size >= g_vol->cluster_size) { + bs->clusters_per_mft_record = g_vol->mft_record_size / + g_vol->cluster_size; + } else { + bs->clusters_per_mft_record = -(ffs(g_vol->mft_record_size) - + 1); + if ((u32)(1 << -bs->clusters_per_mft_record) != + g_vol->mft_record_size) { + free(bs); + ntfs_log_error("BUG: calculated clusters_per_mft_record" + " is wrong (= 0x%x)\n", + bs->clusters_per_mft_record); + return FALSE; + } + } + ntfs_log_debug("clusters per mft record = %i (0x%x)\n", + bs->clusters_per_mft_record, + bs->clusters_per_mft_record); + if (g_vol->indx_record_size >= g_vol->cluster_size) { + bs->clusters_per_index_record = g_vol->indx_record_size / + g_vol->cluster_size; + } else { + bs->clusters_per_index_record = -g_vol->indx_record_size_bits; + if ((1 << -bs->clusters_per_index_record) != + (s32)g_vol->indx_record_size) { + free(bs); + ntfs_log_error("BUG: calculated " + "clusters_per_index_record is wrong " + "(= 0x%x)\n", + bs->clusters_per_index_record); + return FALSE; + } + } + ntfs_log_debug("clusters per index block = %i (0x%x)\n", + bs->clusters_per_index_record, + bs->clusters_per_index_record); + /* Generate a 64-bit random number for the serial number. */ + bs->volume_serial_number = cpu_to_le64(((u64)random() << 32) | + ((u64)random() & 0xffffffff)); + /* + * Leave zero for now as NT4 leaves it zero, too. If want it later, see + * ../libntfs/bootsect.c for how to calculate it. + */ + bs->checksum = cpu_to_le32(0); + /* Make sure the bootsector is ok. */ + if (!ntfs_boot_sector_is_ntfs(bs)) { + free(bs); + ntfs_log_error("FATAL: Generated boot sector is invalid!\n"); + return FALSE; + } + err = add_attr_data_positioned(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), g_rl_boot, (u8*)bs, 8192); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_Boot, FILE_Boot), + (8192 + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1), 8192, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$Boot", FILE_NAME_WIN32_AND_DOS); + if (!err) { + init_system_file_sd(FILE_Boot, &sd, &i); + err = add_attr_sd(m, sd, i); + } + if (err < 0) { + free(bs); + ntfs_log_error("Couldn't create $Boot: %s\n", strerror(-err)); + return FALSE; + } + if (create_backup_boot_sector((u8*)bs)) { + /* + * Pre-2.6 kernels couldn't access the last sector if it was + * odd and we failed to set the device block size to the sector + * size, hence we schedule chkdsk to create it. + */ + volume_flags |= VOLUME_IS_DIRTY; + } + free(bs); + /* + * We cheat a little here and if the user has requested all times to be + * set to zero then we set the GUID to zero as well. This options is + * only used for development purposes so that should be fine. + */ + if (!opts.use_epoch_time) { + /* Generate a GUID for the volume. */ +#ifdef ENABLE_UUID + uuid_generate((void*)&vol_guid); +#else + ntfs_generate_guid(&vol_guid); +#endif + } else + memset(&vol_guid, 0, sizeof(vol_guid)); + if (!create_file_volume(m, root_ref, volume_flags, &vol_guid)) + return FALSE; + ntfs_log_verbose("Creating $BadClus (mft record 8)\n"); + m = (MFT_RECORD*)(g_buf + 8 * g_vol->mft_record_size); + /* FIXME: This should be IGNORE_CASE */ + /* Create a sparse named stream of size equal to the volume size. */ + err = add_attr_data_positioned(m, "$Bad", 4, CASE_SENSITIVE, + const_cpu_to_le16(0), g_rl_bad, NULL, + g_vol->nr_clusters * g_vol->cluster_size); + if (!err) { + err = add_attr_data(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), NULL, 0); + } + if (!err) { + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_BadClus, FILE_BadClus), + 0LL, 0LL, FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, + 0, 0, "$BadClus", FILE_NAME_WIN32_AND_DOS); + } + if (err < 0) { + ntfs_log_error("Couldn't create $BadClus: %s\n", + strerror(-err)); + return FALSE; + } + /* create $Secure (NTFS 3.0+) */ + ntfs_log_verbose("Creating $Secure (mft record 9)\n"); + m = (MFT_RECORD*)(g_buf + 9 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(9, 9), 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_VIEW_INDEX_PRESENT, 0, 0, + "$Secure", FILE_NAME_WIN32_AND_DOS); + buf_sds = NULL; + buf_sds_first_size = 0; + if (!err) { + int buf_sds_size; + + buf_sds_first_size = 0xfc; + buf_sds_size = 0x40000 + buf_sds_first_size; + buf_sds = ntfs_calloc(buf_sds_size); + if (!buf_sds) + return FALSE; + init_secure_sds(buf_sds); + memcpy(buf_sds + 0x40000, buf_sds, buf_sds_first_size); + err = add_attr_data(m, "$SDS", 4, CASE_SENSITIVE, + const_cpu_to_le16(0), (u8*)buf_sds, + buf_sds_size); + } + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$SDH", 4, CASE_SENSITIVE, + AT_UNUSED, COLLATION_NTOFS_SECURITY_HASH, + g_vol->indx_record_size); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$SII", 4, CASE_SENSITIVE, + AT_UNUSED, COLLATION_NTOFS_ULONG, + g_vol->indx_record_size); + if (!err) + err = initialize_secure(buf_sds, buf_sds_first_size, m); + free(buf_sds); + if (err < 0) { + ntfs_log_error("Couldn't create $Secure: %s\n", + strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $UpCase (mft record 0xa)\n"); + m = (MFT_RECORD*)(g_buf + 0xa * g_vol->mft_record_size); + err = add_attr_data(m, NULL, 0, CASE_SENSITIVE, const_cpu_to_le16(0), + (u8*)g_vol->upcase, g_vol->upcase_len << 1); + /* + * The $Info only exists since Windows 8, but it apparently + * does not disturb chkdsk from earlier versions. + */ + if (!err) + err = add_attr_data(m, "$Info", 5, CASE_SENSITIVE, + const_cpu_to_le16(0), + (u8*)g_upcaseinfo, sizeof(struct UPCASEINFO)); + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(FILE_UpCase, FILE_UpCase), + ((g_vol->upcase_len << 1) + + g_vol->cluster_size - 1) & + ~(g_vol->cluster_size - 1), + g_vol->upcase_len << 1, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, + "$UpCase", FILE_NAME_WIN32_AND_DOS); + if (err < 0) { + ntfs_log_error("Couldn't create $UpCase: %s\n", strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $Extend (mft record 11)\n"); + /* + * $Extend index must be resident. Otherwise, w2k3 will regard the + * volume as corrupt. (ERSO) + */ + m = (MFT_RECORD*)(g_buf + 11 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_DIRECTORY; + if (!err) + err = create_hardlink(g_index_block, root_ref, m, + MK_LE_MREF(11, 11), 0LL, 0LL, + FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_I30_INDEX_PRESENT, 0, 0, + "$Extend", FILE_NAME_WIN32_AND_DOS); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$I30", 4, CASE_SENSITIVE, + AT_FILE_NAME, COLLATION_FILE_NAME, + g_vol->indx_record_size); + if (err < 0) { + ntfs_log_error("Couldn't create $Extend: %s\n", + strerror(-err)); + return FALSE; + } + /* NTFS reserved system files (mft records 0xc-0xf) */ + for (i = 0xc; i < 0x10; i++) { + ntfs_log_verbose("Creating system file (mft record 0x%x)\n", i); + m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); + err = add_attr_data(m, NULL, 0, CASE_SENSITIVE, + const_cpu_to_le16(0), NULL, 0); + if (!err) { + init_system_file_sd(i, &sd, &j); + err = add_attr_sd(m, sd, j); + } + if (err < 0) { + ntfs_log_error("Couldn't create system file %i (0x%x): " + "%s\n", i, i, strerror(-err)); + return FALSE; + } + } + /* create systemfiles for ntfs volumes (3.1) */ + /* starting with file 24 (ignoring file 16-23) */ + extend_flags = FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | + FILE_ATTR_ARCHIVE | FILE_ATTR_VIEW_INDEX_PRESENT; + ntfs_log_verbose("Creating $Quota (mft record 24)\n"); + m = (MFT_RECORD*)(g_buf + 24 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_4; + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink_res((MFT_RECORD*)(g_buf + + 11 * g_vol->mft_record_size), extend_ref, m, + MK_LE_MREF(24, 1), 0LL, 0LL, extend_flags, + 0, 0, "$Quota", FILE_NAME_WIN32_AND_DOS); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$Q", 2, CASE_SENSITIVE, AT_UNUSED, + COLLATION_NTOFS_ULONG, g_vol->indx_record_size); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$O", 2, CASE_SENSITIVE, AT_UNUSED, + COLLATION_NTOFS_SID, g_vol->indx_record_size); + if (!err) + err = initialize_quota(m); + if (err < 0) { + ntfs_log_error("Couldn't create $Quota: %s\n", strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $ObjId (mft record 25)\n"); + m = (MFT_RECORD*)(g_buf + 25 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_4; + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink_res((MFT_RECORD*)(g_buf + + 11 * g_vol->mft_record_size), extend_ref, + m, MK_LE_MREF(25, 1), 0LL, 0LL, + extend_flags, 0, 0, "$ObjId", + FILE_NAME_WIN32_AND_DOS); + + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$O", 2, CASE_SENSITIVE, AT_UNUSED, + COLLATION_NTOFS_ULONGS, + g_vol->indx_record_size); + if (!err && opts.with_uuid) + err = index_obj_id_insert(m, &vol_guid, + MK_LE_MREF(FILE_Volume, FILE_Volume)); + if (err < 0) { + ntfs_log_error("Couldn't create $ObjId: %s\n", + strerror(-err)); + return FALSE; + } + ntfs_log_verbose("Creating $Reparse (mft record 26)\n"); + m = (MFT_RECORD*)(g_buf + 26 * g_vol->mft_record_size); + m->flags |= MFT_RECORD_IS_4; + m->flags |= MFT_RECORD_IS_VIEW_INDEX; + if (!err) + err = create_hardlink_res((MFT_RECORD*)(g_buf + + 11 * g_vol->mft_record_size), + extend_ref, m, MK_LE_MREF(26, 1), + 0LL, 0LL, extend_flags, 0, 0, + "$Reparse", FILE_NAME_WIN32_AND_DOS); + /* FIXME: This should be IGNORE_CASE */ + if (!err) + err = add_attr_index_root(m, "$R", 2, CASE_SENSITIVE, AT_UNUSED, + COLLATION_NTOFS_ULONGS, g_vol->indx_record_size); + if (err < 0) { + ntfs_log_error("Couldn't create $Reparse: %s\n", + strerror(-err)); + return FALSE; + } + return TRUE; +} + +/** + * mkntfs_redirect + */ +static int mkntfs_redirect(struct mkntfs_options *opts2) +{ + u64 upcase_crc; + int result = 1; + ntfs_attr_search_ctx *ctx = NULL; + long long lw, pos; + ATTR_RECORD *a; + MFT_RECORD *m; + int i, err; + + if (!opts2) { + ntfs_log_error("Internal error: invalid parameters to mkntfs_options.\n"); + goto done; + } + /* Initialize the random number generator with the current time. */ + srandom(le64_to_cpu(mkntfs_time())/10000000); + /* Allocate and initialize ntfs_volume structure g_vol. */ + g_vol = ntfs_volume_alloc(); + if (!g_vol) { + ntfs_log_perror("Could not create volume"); + goto done; + } + /* Create NTFS 3.1 (Windows XP/Vista) volumes. */ + g_vol->major_ver = 3; + g_vol->minor_ver = 1; + /* Transfer some options to the volume. */ + if (opts.label) { + g_vol->vol_name = strdup(opts.label); + if (!g_vol->vol_name) { + ntfs_log_perror("Could not copy volume name"); + goto done; + } + } + if (opts.cluster_size >= 0) + g_vol->cluster_size = opts.cluster_size; + /* Length is in unicode characters. */ + g_vol->upcase_len = ntfs_upcase_build_default(&g_vol->upcase); + /* Since Windows 8, there is a $Info stream in $UpCase */ + g_upcaseinfo = + (struct UPCASEINFO*)ntfs_malloc(sizeof(struct UPCASEINFO)); + if (!g_vol->upcase_len || !g_upcaseinfo) + goto done; + /* If the CRC is correct, chkdsk does not warn about obsolete table */ + crc64(0,(byte*)NULL,0); /* initialize the crc computation */ + upcase_crc = crc64(0,(byte*)g_vol->upcase, + g_vol->upcase_len * sizeof(ntfschar)); + /* keep the version fields as zero */ + memset(g_upcaseinfo, 0, sizeof(struct UPCASEINFO)); + g_upcaseinfo->len = cpu_to_le32(sizeof(struct UPCASEINFO)); + g_upcaseinfo->crc = cpu_to_le64(upcase_crc); + g_vol->attrdef = ntfs_malloc(sizeof(attrdef_ntfs3x_array)); + if (!g_vol->attrdef) { + ntfs_log_perror("Could not create attrdef structure"); + goto done; + } + memcpy(g_vol->attrdef, attrdef_ntfs3x_array, + sizeof(attrdef_ntfs3x_array)); + g_vol->attrdef_len = sizeof(attrdef_ntfs3x_array); + /* Open the partition. */ + if (!mkntfs_open_partition(g_vol)) + goto done; + /* + * Decide on the sector size, cluster size, mft record and index record + * sizes as well as the number of sectors/tracks/heads/size, etc. + */ + if (!mkntfs_override_vol_params(g_vol)) + goto done; + /* Initialize $Bitmap and $MFT/$BITMAP related stuff. */ + if (!mkntfs_initialize_bitmaps()) + goto done; + /* Initialize MFT & set g_logfile_lcn. */ + if (!mkntfs_initialize_rl_mft()) + goto done; + /* Initialize $LogFile. */ + if (!mkntfs_initialize_rl_logfile()) + goto done; + /* Initialize $Boot. */ + if (!mkntfs_initialize_rl_boot()) + goto done; + /* Allocate a buffer large enough to hold the mft. */ + g_buf = ntfs_calloc(g_mft_size); + if (!g_buf) + goto done; + /* Create runlist for $BadClus, $DATA named stream $Bad. */ + if (!mkntfs_initialize_rl_bad()) + goto done; + /* If not quick format, fill the device with 0s. */ + if (!opts.quick_format) { + if (!mkntfs_fill_device_with_zeroes()) + goto done; + } + /* Create NTFS volume structures. */ + if (!mkntfs_create_root_structures()) + goto done; + /* + * - Do not step onto bad blocks!!! + * - If any bad blocks were specified or found, modify $BadClus, + * allocating the bad clusters in $Bitmap. + * - C&w bootsector backup bootsector (backup in last sector of the + * partition). + * - If NTFS 3.0+, c&w $Secure file and $Extend directory with the + * corresponding special files in it, i.e. $ObjId, $Quota, $Reparse, + * and $UsnJrnl. And others? Or not all necessary? + * - RE: Populate $root with the system files (and $Extend directory if + * applicable). Possibly should move this as far to the top as + * possible and update during each subsequent c&w of each system file. + */ + ntfs_log_verbose("Syncing root directory index record.\n"); + if (!mkntfs_sync_index_record(g_index_block, (MFT_RECORD*)(g_buf + 5 * + g_vol->mft_record_size), NTFS_INDEX_I30, 4)) + goto done; + + ntfs_log_verbose("Syncing $Bitmap.\n"); + m = (MFT_RECORD*)(g_buf + 6 * g_vol->mft_record_size); + + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_perror("Could not create an attribute search context"); + goto done; + } + + if (mkntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + ntfs_log_error("BUG: $DATA attribute not found.\n"); + goto done; + } + + a = ctx->attr; + if (a->non_resident) { + runlist *rl = ntfs_mapping_pairs_decompress(g_vol, a, NULL); + if (!rl) { + ntfs_log_error("ntfs_mapping_pairs_decompress() failed\n"); + goto done; + } + lw = ntfs_rlwrite(g_vol->dev, rl, (const u8*)NULL, + g_lcn_bitmap_byte_size, NULL, WRITE_BITMAP); + err = errno; + free(rl); + if (lw != g_lcn_bitmap_byte_size) { + ntfs_log_error("ntfs_rlwrite: %s\n", lw == -1 ? + strerror(err) : "unknown error"); + goto done; + } + } else { + /* Error : the bitmap must be created non resident */ + ntfs_log_error("Error : the global bitmap is resident\n"); + goto done; + } + + /* + * No need to sync $MFT/$BITMAP as that has never been modified since + * its creation. + */ + ntfs_log_verbose("Syncing $MFT.\n"); + pos = g_mft_lcn * g_vol->cluster_size; + lw = 1; + for (i = 0; i < g_mft_size / (s32)g_vol->mft_record_size; i++) { + if (!opts.no_action) + lw = ntfs_mst_pwrite(g_vol->dev, pos, 1, g_vol->mft_record_size, g_buf + i * g_vol->mft_record_size); + if (lw != 1) { + ntfs_log_error("ntfs_mst_pwrite: %s\n", lw == -1 ? + strerror(errno) : "unknown error"); + goto done; + } + pos += g_vol->mft_record_size; + } + ntfs_log_verbose("Updating $MFTMirr.\n"); + pos = g_mftmirr_lcn * g_vol->cluster_size; + lw = 1; + for (i = 0; i < g_rl_mftmirr[0].length * g_vol->cluster_size / g_vol->mft_record_size; i++) { + m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); + /* + * Decrement the usn by one, so it becomes the same as the one + * in $MFT once it is mst protected. - This is as we need the + * $MFTMirr to have the exact same byte by byte content as + * $MFT, rather than just equivalent meaning content. + */ + if (ntfs_mft_usn_dec(m)) { + ntfs_log_error("ntfs_mft_usn_dec"); + goto done; + } + if (!opts.no_action) + lw = ntfs_mst_pwrite(g_vol->dev, pos, 1, g_vol->mft_record_size, g_buf + i * g_vol->mft_record_size); + if (lw != 1) { + ntfs_log_error("ntfs_mst_pwrite: %s\n", lw == -1 ? + strerror(errno) : "unknown error"); + goto done; + } + pos += g_vol->mft_record_size; + } + ntfs_log_verbose("Syncing device.\n"); + if (g_vol->dev->d_ops->sync(g_vol->dev)) { + ntfs_log_error("Syncing device. FAILED"); + goto done; + } + ntfs_log_quiet("mkntfs completed successfully. Have a nice day.\n"); + result = 0; +done: + ntfs_attr_put_search_ctx(ctx); + mkntfs_cleanup(); /* Device is unlocked and closed here */ + return result; +} + + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + int result = 1; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + utils_set_locale(); + + mkntfs_init_options(&opts); /* Set up the options */ + + /* Read the command line options */ + result = mkntfs_parse_options(argc, argv, &opts); + + if (result < 0) + result = mkntfs_redirect(&opts); + + return result; +} diff --git a/ntfsprogs/ntfscat.8 b/ntfsprogs/ntfscat.8 new file mode 100755 index 0000000000000000000000000000000000000000..3ca182363fd92dca87dd7f7b9c761ba8f6040b78 --- /dev/null +++ b/ntfsprogs/ntfscat.8 @@ -0,0 +1,136 @@ +.\" Copyright (c) 2003\-2005 Richard Russon. +.\" Copyright (c) 2007 Yura Pakhuchiy. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCAT 8 "September 2007" "ntfs-3g 2015.3.14" +.SH NAME +ntfscat \- print NTFS files and streams on the standard output +.SH SYNOPSIS +[\fIoptions\fR] \fIdevice \fR[\fIfile\fR] +.SH DESCRIPTION +.B ntfscat +will read a file or stream from an NTFS volume and display the contents +on the standard output. +.PP +The case of the filename passed to +.B ntfscat +is ignored. +.SH OPTIONS +Below is a summary of all the options that +.B ntfscat +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-a\fR, \fB\-\-attribute\fR TYPE +Display the contents of a particular attribute type. By default, the unnamed +$DATA attribute will be shown. The attribute can be specified by a number +in decimal or hexadecimal, or by name. +.TS +box; +lB lB lB +l l l. +Hex Decimal Name +0x10 16 "$STANDARD_INFORMATION" +0x20 32 "$ATTRIBUTE_LIST" +0x30 48 "$FILE_NAME" +0x40 64 "$OBJECT_ID" +0x50 80 "$SECURITY_DESCRIPTOR" +0x60 96 "$VOLUME_NAME" +0x70 112 "$VOLUME_INFORMATION" +0x80 128 "$DATA" +0x90 144 "$INDEX_ROOT" +0xA0 160 "$INDEX_ALLOCATION" +0xB0 176 "$BITMAP" +0xC0 192 "$REPARSE_POINT" +0xD0 208 "$EA_INFORMATION" +0xE0 224 "$EA" +0xF0 240 "$PROPERTY_SET" +0x100 256 "$LOGGED_UTILITY_STREAM" +.TE +.sp +.sp +.B Notes +The attribute names may be given without the leading $ symbol. +.br +If you use the $ symbol, you must quote the name to prevent the shell +interpreting the name. +.TP +\fB\-n\fR, \fB\-\-attribute\-name\fR NAME +Display this named attribute, stream. +.TP +\fB\-i\fR, \fB\-\-inode\fR NUM +Specify a file by its inode number instead of its name. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not using a mounted volume. +Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license +.BR ntfscat . +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.SH EXAMPLES +Display the contents of a file in the root of an NTFS volume. +.RS +.sp +.B ntfscat /dev/hda1 boot.ini +.sp +.RE +Display the contents of a file in a subdirectory of an NTFS volume. +.RS +.sp +.B ntfscat /dev/hda1 /winnt/system32/drivers/etc/hosts +.sp +.RE +Display the contents of the $INDEX_ROOT attribute of the root directory (inode +5). +.RS +.sp +.B ntfscat /dev/hda1 \-a INDEX_ROOT \-i 5 | hexdump \-C +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfscat . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfscat +was written by Richard Russon, Anton Altaparmakov and Szabolcs Szakacsits. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfscat +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +Read \fBlibntfs\fR(8) for details how to access encrypted files. +.sp +.BR libntfs (8), +.BR ntfsls (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscat.8.in b/ntfsprogs/ntfscat.8.in new file mode 100755 index 0000000000000000000000000000000000000000..478d9b4b233983296463a8ad395135ea79e6fd92 --- /dev/null +++ b/ntfsprogs/ntfscat.8.in @@ -0,0 +1,136 @@ +.\" Copyright (c) 2003\-2005 Richard Russon. +.\" Copyright (c) 2007 Yura Pakhuchiy. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCAT 8 "September 2007" "ntfs-3g @VERSION@" +.SH NAME +ntfscat \- print NTFS files and streams on the standard output +.SH SYNOPSIS +[\fIoptions\fR] \fIdevice \fR[\fIfile\fR] +.SH DESCRIPTION +.B ntfscat +will read a file or stream from an NTFS volume and display the contents +on the standard output. +.PP +The case of the filename passed to +.B ntfscat +is ignored. +.SH OPTIONS +Below is a summary of all the options that +.B ntfscat +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-a\fR, \fB\-\-attribute\fR TYPE +Display the contents of a particular attribute type. By default, the unnamed +$DATA attribute will be shown. The attribute can be specified by a number +in decimal or hexadecimal, or by name. +.TS +box; +lB lB lB +l l l. +Hex Decimal Name +0x10 16 "$STANDARD_INFORMATION" +0x20 32 "$ATTRIBUTE_LIST" +0x30 48 "$FILE_NAME" +0x40 64 "$OBJECT_ID" +0x50 80 "$SECURITY_DESCRIPTOR" +0x60 96 "$VOLUME_NAME" +0x70 112 "$VOLUME_INFORMATION" +0x80 128 "$DATA" +0x90 144 "$INDEX_ROOT" +0xA0 160 "$INDEX_ALLOCATION" +0xB0 176 "$BITMAP" +0xC0 192 "$REPARSE_POINT" +0xD0 208 "$EA_INFORMATION" +0xE0 224 "$EA" +0xF0 240 "$PROPERTY_SET" +0x100 256 "$LOGGED_UTILITY_STREAM" +.TE +.sp +.sp +.B Notes +The attribute names may be given without the leading $ symbol. +.br +If you use the $ symbol, you must quote the name to prevent the shell +interpreting the name. +.TP +\fB\-n\fR, \fB\-\-attribute\-name\fR NAME +Display this named attribute, stream. +.TP +\fB\-i\fR, \fB\-\-inode\fR NUM +Specify a file by its inode number instead of its name. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not using a mounted volume. +Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license +.BR ntfscat . +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.SH EXAMPLES +Display the contents of a file in the root of an NTFS volume. +.RS +.sp +.B ntfscat /dev/hda1 boot.ini +.sp +.RE +Display the contents of a file in a subdirectory of an NTFS volume. +.RS +.sp +.B ntfscat /dev/hda1 /winnt/system32/drivers/etc/hosts +.sp +.RE +Display the contents of the $INDEX_ROOT attribute of the root directory (inode +5). +.RS +.sp +.B ntfscat /dev/hda1 \-a INDEX_ROOT \-i 5 | hexdump \-C +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfscat . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfscat +was written by Richard Russon, Anton Altaparmakov and Szabolcs Szakacsits. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfscat +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +Read \fBlibntfs\fR(8) for details how to access encrypted files. +.sp +.BR libntfs (8), +.BR ntfsls (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscat.c b/ntfsprogs/ntfscat.c new file mode 100755 index 0000000000000000000000000000000000000000..2ae1f9cdc0f5a5bf047dee54c1ae739a38fa6110 --- /dev/null +++ b/ntfsprogs/ntfscat.c @@ -0,0 +1,444 @@ +/** + * ntfscat - Part of the Linux-NTFS project. + * + * Copyright (c) 2003-2005 Richard Russon + * Copyright (c) 2003-2005 Anton Altaparmakov + * Copyright (c) 2003-2005 Szabolcs Szakacsits + * Copyright (c) 2007 Yura Pakhuchiy + * + * This utility will concatenate files and print on the standard output. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "types.h" +#include "attrib.h" +#include "utils.h" +#include "volume.h" +#include "debug.h" +#include "dir.h" +#include "ntfscat.h" +/* #include "version.h" */ +#include "utils.h" + +static const char *EXEC_NAME = "ntfscat"; +static struct options opts; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Concatenate files and print " + "on the standard output.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2003-2005 Richard Russon\n"); + ntfs_log_info("Copyright (c) 2003-2005 Anton Altaparmakov\n"); + ntfs_log_info("Copyright (c) 2003-2005 Szabolcs Szakacsits\n"); + ntfs_log_info("Copyright (c) 2007 Yura Pakhuchiy\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device [file]\n\n" + " -a, --attribute TYPE Display this attribute type\n" + " -n, --attribute-name NAME Display this attribute name\n" + " -i, --inode NUM Display this inode\n\n" + " -f, --force Use less caution\n" + " -h, --help Print this help\n" + " -q, --quiet Less output\n" + " -V, --version Version information\n" + " -v, --verbose More output\n\n", +// Does not work for compressed files at present so leave undocumented... +// " -r --raw Display the raw data (e.g. for compressed or encrypted file)", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_attribute - Read an attribute name, or number + * @value: String to be parsed + * @attr: Resulting attribute id (on success) + * + * Read a string representing an attribute. It may be a decimal, octal or + * hexadecimal number, or the attribute name in full. The leading $ sign is + * optional. + * + * Return: 1 Success, a valid attribute name or number + * 0 Error, not an attribute name or number + */ +static int parse_attribute(const char *value, ATTR_TYPES *attr) +{ + static const char *attr_name[] = { + "$STANDARD_INFORMATION", + "$ATTRIBUTE_LIST", + "$FILE_NAME", + "$OBJECT_ID", + "$SECURITY_DESCRIPTOR", + "$VOLUME_NAME", + "$VOLUME_INFORMATION", + "$DATA", + "$INDEX_ROOT", + "$INDEX_ALLOCATION", + "$BITMAP", + "$REPARSE_POINT", + "$EA_INFORMATION", + "$EA", + "$PROPERTY_SET", + "$LOGGED_UTILITY_STREAM", + NULL + }; + + int i; + long num; + + for (i = 0; attr_name[i]; i++) { + if ((strcmp(value, attr_name[i]) == 0) || + (strcmp(value, attr_name[i] + 1) == 0)) { + *attr = (ATTR_TYPES)cpu_to_le32((i + 1) * 16); + return 1; + } + } + + num = strtol(value, NULL, 0); + if ((num > 0) && (num < 257)) { + *attr = (ATTR_TYPES)cpu_to_le32(num); + return 1; + } + + return 0; +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-a:fh?i:n:qVvr"; + static const struct option lopt[] = { + { "attribute", required_argument, NULL, 'a' }, + { "attribute-name", required_argument, NULL, 'n' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "inode", required_argument, NULL, 'i' }, + { "quiet", no_argument, NULL, 'q' }, + { "version", no_argument, NULL, 'V' }, + { "verbose", no_argument, NULL, 'v' }, + { "raw", no_argument, NULL, 'r' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + ATTR_TYPES attr = AT_UNUSED; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.inode = -1; + opts.attr = cpu_to_le32(-1); + opts.attr_name = NULL; + opts.attr_name_len = 0; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind - 1]; + } else if (!opts.file) { + opts.file = argv[optind - 1]; + } else { + ntfs_log_error("You must specify exactly one " + "file.\n"); + err++; + } + break; + case 'a': + if (opts.attr != cpu_to_le32(-1)) { + ntfs_log_error("You must specify exactly one " + "attribute.\n"); + } else if (parse_attribute(optarg, &attr) > 0) { + opts.attr = attr; + break; + } else { + ntfs_log_error("Couldn't parse attribute.\n"); + } + err++; + break; + case 'f': + opts.force++; + break; + case 'h': + help++; + break; + case 'i': + if (opts.inode != -1) + ntfs_log_error("You must specify exactly one inode.\n"); + else if (utils_parse_size(optarg, &opts.inode, FALSE)) + break; + else + ntfs_log_error("Couldn't parse inode number.\n"); + err++; + break; + + case 'n': + opts.attr_name_len = ntfs_mbstoucs(optarg, + &opts.attr_name); + if (opts.attr_name_len < 0) { + ntfs_log_perror("Invalid attribute name '%s'", + optarg); + usage(); + } + + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'V': + ver++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'r': + opts.raw = TRUE; + break; + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + /* fall through */ + default: + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.device == NULL) { + ntfs_log_error("You must specify a device.\n"); + err++; + + } else if (opts.file == NULL && opts.inode == -1) { + ntfs_log_error("You must specify a file or inode " + "with the -i option.\n"); + err++; + + } else if (opts.file != NULL && opts.inode != -1) { + ntfs_log_error("You can't specify both a file and inode.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the " + "same time.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + /* tri-state 0 : done, 1 : error, -1 : proceed */ + return (err ? 1 : (help || ver ? 0 : -1)); +} + +/** + * index_get_size - Find the INDX block size from the index root + * @inode: Inode of the directory to be checked + * + * Find the size of a directory's INDX block from the INDEX_ROOT attribute. + * + * Return: n Success, the INDX blocks are n bytes in size + * 0 Error, not a directory + */ +static int index_get_size(ntfs_inode *inode) +{ + ATTR_RECORD *attr90; + INDEX_ROOT *iroot; + + attr90 = find_first_attribute(AT_INDEX_ROOT, inode->mrec); + if (!attr90) + return 0; // not a directory + + iroot = (INDEX_ROOT*)((u8*)attr90 + le16_to_cpu(attr90->value_offset)); + return le32_to_cpu(iroot->index_block_size); +} + +/** + * cat + */ +static int cat(ntfs_volume *vol, ntfs_inode *inode, ATTR_TYPES type, + ntfschar *name, int namelen) +{ + const int bufsize = 4096; + char *buffer; + ntfs_attr *attr; + s64 bytes_read, written; + s64 offset; + u32 block_size; + + buffer = malloc(bufsize); + if (!buffer) + return 1; + + attr = ntfs_attr_open(inode, type, name, namelen); + if (!attr) { + ntfs_log_error("Cannot find attribute type 0x%x.\n", + le32_to_cpu(type)); + free(buffer); + return 1; + } + + if ((inode->mft_no < 2) && (attr->type == AT_DATA)) + block_size = vol->mft_record_size; + else if (attr->type == AT_INDEX_ALLOCATION) + block_size = index_get_size(inode); + else + block_size = 0; + + offset = 0; + for (;;) { + if (!opts.raw && block_size > 0) { + // These types have fixup + bytes_read = ntfs_attr_mst_pread(attr, offset, 1, block_size, buffer); + if (bytes_read > 0) + bytes_read *= block_size; + } else { + bytes_read = ntfs_attr_pread(attr, offset, bufsize, buffer); + } + //ntfs_log_info("read %lld bytes\n", bytes_read); + if (bytes_read == -1) { + ntfs_log_perror("ERROR: Couldn't read file"); + break; + } + if (!bytes_read) + break; + + written = fwrite(buffer, 1, bytes_read, stdout); + if (written != bytes_read) { + ntfs_log_perror("ERROR: Couldn't output all data!"); + break; + } + offset += bytes_read; + } + + ntfs_attr_close(attr); + free(buffer); + return 0; +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + ntfs_volume *vol; + ntfs_inode *inode; + ATTR_TYPES attr; + int res; + int result = 1; + + ntfs_log_set_handler(ntfs_log_handler_stderr); + + res = parse_options(argc, argv); + if (res >= 0) + return (res); + + utils_set_locale(); + + vol = utils_mount_volume(opts.device, NTFS_MNT_RDONLY | + (opts.force ? NTFS_MNT_RECOVER : 0)); + if (!vol) { + ntfs_log_perror("ERROR: couldn't mount volume"); + return 1; + } + + if (opts.inode != -1) + inode = ntfs_inode_open(vol, opts.inode); + else + inode = ntfs_pathname_to_inode(vol, NULL, opts.file); + + if (!inode) { + ntfs_log_perror("ERROR: Couldn't open inode"); + return 1; + } + + attr = AT_DATA; + if (opts.attr != cpu_to_le32(-1)) + attr = opts.attr; + + result = cat(vol, inode, attr, opts.attr_name, opts.attr_name_len); + + ntfs_inode_close(inode); + ntfs_umount(vol, FALSE); + + return result; +} diff --git a/ntfsprogs/ntfscat.h b/ntfsprogs/ntfscat.h new file mode 100755 index 0000000000000000000000000000000000000000..cf474b4889dd3908858dee99aa49c5369fb98d2c --- /dev/null +++ b/ntfsprogs/ntfscat.h @@ -0,0 +1,46 @@ +/* + * ntfscat - Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Richard Russon + * Copyright (c) 2003 Anton Altaparmakov + * + * This utility will concatenate files and print on the standard output. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFSCAT_H_ +#define _NTFSCAT_H_ + +#include "types.h" +#include "layout.h" + +struct options { + char *device; /* Device/File to work with */ + char *file; /* File to display */ + s64 inode; /* Inode to work with */ + ATTR_TYPES attr; /* Attribute type to display */ + ntfschar *attr_name; /* Attribute name to display */ + int attr_name_len; /* Attribute name length */ + int force; /* Override common sense */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + BOOL raw; /* Raw data output */ +}; + +#endif /* _NTFSCAT_H_ */ + + diff --git a/ntfsprogs/ntfsck.c b/ntfsprogs/ntfsck.c new file mode 100755 index 0000000000000000000000000000000000000000..278160190dc3345e8058467fccc59e0ddef14511 --- /dev/null +++ b/ntfsprogs/ntfsck.c @@ -0,0 +1,883 @@ +/** + * ntfsck - Part of the Linux-NTFS project. + * + * Copyright (c) 2006 Yuval Fledel + * + * This utility will check and fix errors on an NTFS volume. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include <layout.h> +#include <bitmap.h> +#include <endians.h> +#include <bootsect.h> +#include <misc.h> + +#include "cluster.h" +#include "utils.h" + +#define RETURN_FS_ERRORS_CORRECTED (1) +#define RETURN_SYSTEM_NEEDS_REBOOT (2) +#define RETURN_FS_ERRORS_LEFT_UNCORRECTED (4) +#define RETURN_OPERATIONAL_ERROR (8) +#define RETURN_USAGE_OR_SYNTAX_ERROR (16) +#define RETURN_CANCELLED_BY_USER (32) +/* Where did 64 go? */ +#define RETURN_SHARED_LIBRARY_ERROR (128) + +/* todo: command line: (everything is optional) + * fsck-frontend options: + * -C [fd] : display progress bar (send it to the file descriptor if specified) + * -T : don't show the title on startup + * fsck-checker options: + * -a : auto-repair. no questions. (optional: if marked clean and -f not specified, just check if mounable) + * -p : auto-repair safe. no questions (optional: same) + * -n : only check. no repair. + * -r : interactively repair. + * -y : always yes. + * -v : verbose. + * -V : version. + * taken from fsck.ext2 + * -b sb : use the superblock from sb. For corrupted volumes. (do we want separete boot/mft options?) + * -c : use badblocks(8) to find bad blocks (R/O mode) and add the findings to $Bad. + * -C fd : write competion info to fd. If 0, print a completion bar. + * -d : debugging output. + * -D : rebalance indices. + * -f : force checking even if marked clean. + * -F : flush buffers before beginning. (for time-benchmarking) + * -k : When used with -c, don't erase previous $Bad items. + * -n : Open fs as readonly. assume always no. (why is it needed if -r is not specified?) + * -t : Print time statistics. + * taken from fsck.reiserfs + * --rebuild-sb : try to find $MFT start and rebuild the boot sector. + * --rebuild-tree : scan for items and rebuild the indices that point to them (0x30, $SDS, etc.) + * --clean-reserved: zero rezerved fields. (use with care!) + * --adjust-size -z: insert a sparse hole if the data_size is larger than the size marked in the runlist. + * --logfile file : report corruptions (unlike other errors) to a file instead of stderr. + * --nolog : don't report corruptions at all. + * --quiet -q : no progress bar. + * taken from fsck.msdos + * -w : flush after every write. + * - do n passes. (only 2 in fsck.msdos. second should not report errors. Bonus: stop when error list does not change) + * taken from fsck.jfs + * --omit-journal-reply: self-descriptive (why would someone do that?) + * --replay-journal-only: self-descriptive. don't check otherwise. + * taken from fsck.xfs + * -s : only serious errors should be reported. + * -i ino : verbose behaviour only for inode ino. + * -b bno : verbose behaviour only for cluster bno. + * -L : zero log. + * inspired by others + * - don't do cluster accounting. + * - don't do mft record accounting. + * - don't do file names accounting. + * - don't do security_id accounting. + * - don't check acl inheritance problems. + * - undelete unused mft records. (bonus: different options for 100% salvagable and less) + * - error-level-report n: only report errors above this error level + * - error-level-repair n: only repair errors below this error level + * - don't fail on ntfsclone metadata pruning. + * signals: + * SIGUSR1 : start displaying progress bar + * SIGUSR2 : stop displaying progress bar. + */ + +/* Assuming NO_NTFS_DEVICE_DEFAULT_IO_OPS is not set */ + +static int errors = 0; +static int unsupported = 0; + +static short bytes_per_sector, sectors_per_cluster; +//static s64 mft_offset, mftmirr_offset; +static s64 current_mft_record; + +/** + * This is just a preliminary volume. + * Filled while checking the boot sector and used in the preliminary MFT check. + */ +//static ntfs_volume vol; + +static runlist_element *mft_rl, *mft_bitmap_rl; + +#define check_failed(FORMAT, ARGS...) \ + do { \ + errors++; \ + ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__, \ + NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS); \ + } while (0); + +/** + * 0 success. + * 1 fail. + */ +static int assert_u32_equal(u32 val, u32 ok, const char *name) +{ + if (val!=ok) { + check_failed("Assertion failed for '%lld:%s'. should be 0x%x, " + "was 0x%x.\n", (long long)current_mft_record, name, + (int)ok, (int)val); + //errors++; + return 1; + } + return 0; +} + +static int assert_u32_noteq(u32 val, u32 wrong, const char *name) +{ + if (val==wrong) { + check_failed("Assertion failed for '%lld:%s'. should not be " + "0x%x.\n", (long long)current_mft_record, name, + (int)wrong); + return 1; + } + return 0; +} + +static int assert_u32_lesseq(u32 val1, u32 val2, const char *name) +{ + if (val1 > val2) { + check_failed("Assertion failed for '%s'. 0x%x > 0x%x\n", + name, (int)val1, (int)val2); + //errors++; + return 1; + } + return 0; +} + +static int assert_u32_less(u32 val1, u32 val2, const char *name) +{ + if (val1 >= val2) { + check_failed("Assertion failed for '%s'. 0x%x >= 0x%x\n", + name, (int)val1, (int)val2); + //errors++; + return 1; + } + return 0; +} + +/** + * Return: 0 ok, 1 error. + * + * todo: may we use ntfs_boot_sector_is_ntfs() instead? + * It already does the checks but will not be able to fix anything. + */ +static BOOL verify_boot_sector(struct ntfs_device *dev, ntfs_volume *rawvol) +{ + u8 buf[512]; + NTFS_BOOT_SECTOR *ntfs_boot = (NTFS_BOOT_SECTOR *)&buf; + //u32 bytes_per_cluster; + + current_mft_record = 9; + + if (ntfs_pread(dev, 0, sizeof(buf), buf) != sizeof(buf)) { + check_failed("Failed to read boot sector.\n"); + return 1; + } + + if ((buf[0]!=0xeb) || + ((buf[1]!=0x52) && (buf[1]!=0x5b)) || + (buf[2]!=0x90)) { + check_failed("Boot sector: Bad jump.\n"); + } + if (ntfs_boot->oem_id != magicNTFS) { + check_failed("Boot sector: Bad NTFS magic.\n"); + } + bytes_per_sector = le16_to_cpu(ntfs_boot->bpb.bytes_per_sector); + if (!bytes_per_sector) { + check_failed("Boot sector: Bytes per sector is 0.\n"); + } + if (bytes_per_sector%512) { + check_failed("Boot sector: Bytes per sector is not a multiple" + " of 512.\n"); + } + sectors_per_cluster = ntfs_boot->bpb.sectors_per_cluster; + + // todo: if partition, query bios and match heads/tracks? */ + + // Initialize some values into rawvol. We will need those later. + rawvol->dev = dev; + ntfs_boot_sector_parse(rawvol, (NTFS_BOOT_SECTOR *)buf); + + return 0; +} + +/** + * Load the runlist of the <attr_type> attribute. + * + * Return NULL if an error. + * The caller is responsible on freeing the allocated memory if the result is not NULL. + * + * Set size_of_file_record to some reasonable size when in doubt (the Windows default is 1024.) + * + * attr_type must be little endian. + * + * This function has code duplication with check_file_record() and + * check_attr_record() but its goal is to be less strict. Thus the + * duplicated checks are the minimal required for not crashing. + * + * Assumes dev is open. + */ +static runlist *load_runlist(ntfs_volume *rawvol, s64 offset_to_file_record, u32 attr_type, u32 size_of_file_record) +{ + u8 *buf; + u16 attrs_offset; + u32 length; + ATTR_RECORD *attr_rec; + + if (size_of_file_record<22) // offset to attrs_offset + return NULL; + + buf = (u8*)ntfs_malloc(size_of_file_record); + if (!buf) + return NULL; + + if (ntfs_pread(rawvol->dev, offset_to_file_record, size_of_file_record, buf) != + size_of_file_record) { + check_failed("Failed to read file record at offset %lld (0x%llx).\n", + (long long)offset_to_file_record, + (long long)offset_to_file_record); + return NULL; + } + + attrs_offset = le16_to_cpu(((MFT_RECORD*)buf)->attrs_offset); + // first attribute must be after the header. + if (attrs_offset<42) { + check_failed("First attribute must be after the header (%u).\n", (int)attrs_offset); + } + attr_rec = (ATTR_RECORD *)(buf + attrs_offset); + //printf("uv1.\n"); + + while ((u8*)attr_rec<=buf+size_of_file_record-4) { + + //printf("Attr type: 0x%x.\n", attr_rec->type); + // Check attribute record. (Only what is in the buffer) + if (attr_rec->type==AT_END) { + check_failed("Attribute 0x%x not found in file record at offset %lld (0x%llx).\n", (int)le32_to_cpu(attr_rec->type), + (long long)offset_to_file_record, + (long long)offset_to_file_record); + return NULL; + } + if ((u8*)attr_rec>buf+size_of_file_record-8) { + // not AT_END yet no room for the length field. + check_failed("Attribute 0x%x is not AT_END, yet no " + "room for the length field.\n", + (int)le32_to_cpu(attr_rec->type)); + return NULL; + } + + length = le32_to_cpu(attr_rec->length); + + // Check that this attribute does not overflow the mft_record + if ((u8*)attr_rec+length >= buf+size_of_file_record) { + check_failed("Attribute (0x%x) is larger than FILE record at offset %lld (0x%llx).\n", + (int)le32_to_cpu(attr_rec->type), + (long long)offset_to_file_record, + (long long)offset_to_file_record); + return NULL; + } + // todo: what ATTRIBUTE_LIST (0x20)? + + if (attr_rec->type==attr_type) { + // Eurika! + + // ntfs_mapping_pairs_decompress only use two values from vol. Just fake it. + // todo: it will also use vol->major_ver if defined(DEBUG). But only for printing purposes. + + // Assume ntfs_boot_sector_parse() was called. + return ntfs_mapping_pairs_decompress(rawvol, attr_rec, NULL); + } + + attr_rec = (ATTR_RECORD*)((u8*)attr_rec+length); + } + // If we got here, there was an overflow. + check_failed("file record corrupted at offset %lld (0x%llx).\n", + (long long)offset_to_file_record, + (long long)offset_to_file_record); + return NULL; +} + +/** + * Return: >=0 last VCN + * LCN_EINVAL error. + */ +static VCN get_last_vcn(runlist *rl) +{ + VCN res; + + if (!rl) + return LCN_EINVAL; + + res = LCN_EINVAL; + while (rl->length) { + ntfs_log_verbose("vcn: %lld, length: %lld.\n", + (long long)rl->vcn, (long long)rl->length); + if (rl->vcn<0) + res = rl->vcn; + else + res = rl->vcn + rl->length; + rl++; + } + + return res; +} + +static u32 mft_bitmap_records; +static u8 *mft_bitmap_buf; + +/** + * Assumes mft_bitmap_rl is initialized. + * return: 0 ok. + * RETURN_OPERATIONAL_ERROR on error. + */ +static int mft_bitmap_load(ntfs_volume *rawvol) +{ + VCN vcn; + u32 mft_bitmap_length; + + vcn = get_last_vcn(mft_bitmap_rl); + if (vcn<=LCN_EINVAL) { + mft_bitmap_buf = NULL; + /* This case should not happen, not even with on-disk errors */ + goto error; + } + + mft_bitmap_length = vcn * rawvol->cluster_size; + mft_bitmap_records = 8 * mft_bitmap_length * rawvol->cluster_size / + rawvol->mft_record_size; + + //printf("sizes: %d, %d.\n", mft_bitmap_length, mft_bitmap_records); + + mft_bitmap_buf = (u8*)ntfs_malloc(mft_bitmap_length); + if (!mft_bitmap_buf) + goto error; + if (ntfs_rl_pread(rawvol, mft_bitmap_rl, 0, mft_bitmap_length, + mft_bitmap_buf)!=mft_bitmap_length) + goto error; + return 0; +error: + mft_bitmap_records = 0; + ntfs_log_error("Could not load $MFT/Bitmap.\n"); + return RETURN_OPERATIONAL_ERROR; +} + +/** + * -1 Error. + * 0 Unused record + * 1 Used record + * + * Assumes mft_bitmap_rl was initialized. + */ +static int mft_bitmap_get_bit(s64 mft_no) +{ + if (mft_no>=mft_bitmap_records) + return -1; + return ntfs_bit_get(mft_bitmap_buf, mft_no); +} + +/** + * @attr_rec: The attribute record to check + * @mft_rec: The parent FILE record. + * @buflen: The size of the FILE record. + * + * Return: + * NULL: Fatal error occured. Not sure where is the next record. + * otherwise: pointer to the next attribute record. + * + * The function only check fields that are inside this attr record. + * + * Assumes mft_rec is current_mft_record. + */ +static ATTR_REC *check_attr_record(ATTR_REC *attr_rec, MFT_RECORD *mft_rec, + u16 buflen) +{ + u16 name_offset; + u16 attrs_offset = le16_to_cpu(mft_rec->attrs_offset); + u32 attr_type = le32_to_cpu(attr_rec->type); + u32 length = le32_to_cpu(attr_rec->length); + + // Check that this attribute does not overflow the mft_record + if ((u8*)attr_rec+length >= ((u8*)mft_rec)+buflen) { + check_failed("Attribute (0x%x) is larger than FILE record (%lld).\n", + (int)attr_type, (long long)current_mft_record); + return NULL; + } + + // Attr type must be a multiple of 0x10 and 0x10<=x<=0x100. + if ((attr_type & ~0x0F0) && (attr_type != 0x100)) { + check_failed("Unknown attribute type 0x%x.\n", + (int)attr_type); + goto check_attr_record_next_attr; + } + + if (length<24) { + check_failed("Attribute %lld:0x%x Length too short (%u).\n", + (long long)current_mft_record, (int)attr_type, + (int)length); + goto check_attr_record_next_attr; + } + + // If this is the first attribute: + // todo: instance number must be smaller than next_instance. + if ((u8*)attr_rec == ((u8*)mft_rec) + attrs_offset) { + if (!mft_rec->base_mft_record) + assert_u32_equal(attr_type, 0x10, + "First attribute type"); + // The following not always holds. + // attr 0x10 becomes instance 1 and attr 0x40 becomes 0. + //assert_u32_equal(attr_rec->instance, 0, + // "First attribute instance number"); + } else { + assert_u32_noteq(attr_type, 0x10, + "Not-first attribute type"); + // The following not always holds. + //assert_u32_noteq(attr_rec->instance, 0, + // "Not-first attribute instance number"); + } + //if (current_mft_record==938 || current_mft_record==1683 || current_mft_record==3152 || current_mft_record==22410) + //printf("Attribute %lld:0x%x instance: %u isbase:%d.\n", + // current_mft_record, (int)attr_type, (int)le16_to_cpu(attr_rec->instance), (int)mft_rec->base_mft_record); + // todo: instance is unique. + + // Check flags. + if (attr_rec->flags & ~(const_cpu_to_le16(0xc0ff))) { + check_failed("Attribute %lld:0x%x Unknown flags (0x%x).\n", + (long long)current_mft_record, (int)attr_type, + (int)le16_to_cpu(attr_rec->flags)); + } + + if (attr_rec->non_resident>1) { + check_failed("Attribute %lld:0x%x Unknown non-resident " + "flag (0x%x).\n", (long long)current_mft_record, + (int)attr_type, (int)attr_rec->non_resident); + goto check_attr_record_next_attr; + } + + name_offset = le16_to_cpu(attr_rec->name_offset); + /* + * todo: name must be legal unicode. + * Not really, information below in urls is about filenames, but I + * believe it also applies to attribute names. (Yura) + * http://blogs.msdn.com/michkap/archive/2006/09/24/769540.aspx + * http://blogs.msdn.com/michkap/archive/2006/09/10/748699.aspx + */ + + if (attr_rec->non_resident) { + // Non-Resident + + // Make sure all the fields exist. + if (length<64) { + check_failed("Non-resident attribute %lld:0x%x too short (%u).\n", + (long long)current_mft_record, (int)attr_type, + (int)length); + goto check_attr_record_next_attr; + } + if (attr_rec->compression_unit && (length<72)) { + check_failed("Compressed attribute %lld:0x%x too short (%u).\n", + (long long)current_mft_record, (int)attr_type, + (int)length); + goto check_attr_record_next_attr; + } + + // todo: name comes before mapping pairs, and after the header. + // todo: length==mapping_pairs_offset+length of compressed mapping pairs. + // todo: mapping_pairs_offset is 8-byte aligned. + + // todo: lowest vcn <= highest_vcn + // todo: if base record -> lowest vcn==0 + // todo: lowest_vcn!=0 -> attribute list is used. + // todo: lowest_vcn & highest_vcn are in the drive (0<=x<total clusters) + // todo: mapping pairs agree with highest_vcn. + // todo: compression unit == 0 or 4. + // todo: reserved1 == 0. + // todo: if not compressed nor sparse, initialized_size <= allocated_size and data_size <= allocated_size. + // todo: if compressed or sparse, allocated_size <= initialized_size and allocated_size <= data_size + // todo: if mft_no!=0 and not compressed/sparse, data_size==initialized_size. + // todo: if mft_no!=0 and compressed/sparse, allocated_size==initialized_size. + // todo: what about compressed_size if compressed? + // todo: attribute must not be 0x10, 0x30, 0x40, 0x60, 0x70, 0x90, 0xd0 (not sure about 0xb0, 0xe0, 0xf0) + } else { + u16 value_offset = le16_to_cpu(attr_rec->value_offset); + u32 value_length = le32_to_cpu(attr_rec->value_length); + // Resident + if (attr_rec->name_length) { + if (name_offset < 24) + check_failed("Resident attribute with " + "name intersecting header.\n"); + if (value_offset < name_offset + + attr_rec->name_length) + check_failed("Named resident attribute " + "with value before name.\n"); + } + // if resident, length==value_length+value_offset + //assert_u32_equal(le32_to_cpu(attr_rec->value_length)+ + // value_offset, length, + // "length==value_length+value_offset"); + // if resident, length==value_length+value_offset + if (value_length+value_offset > length) { + check_failed("value_length(%d)+value_offset(%d)>length(%d) for attribute 0x%x.\n", (int)value_length, (int)value_offset, (int)length, (int)attr_type); + return NULL; + } + + // Check resident_flags. + if (attr_rec->resident_flags>0x01) { + check_failed("Unknown resident flags (0x%x) for attribute 0x%x.\n", (int)attr_rec->resident_flags, (int)attr_type); + } else if (attr_rec->resident_flags && (attr_type!=0x30)) { + check_failed("Resident flags mark attribute 0x%x as indexed.\n", (int)attr_type); + } + + // reservedR is 0. + assert_u32_equal(attr_rec->reservedR, 0, "Resident Reserved"); + + // todo: attribute must not be 0xa0 (not sure about 0xb0, 0xe0, 0xf0) + // todo: check content well-formness per attr_type. + } + return 0; +check_attr_record_next_attr: + return (ATTR_REC *)(((u8 *)attr_rec) + length); +} + +/** + * All checks that can be satisfied only by data from the buffer. + * No other [MFT records/metadata files] are required. + * + * The buffer is changed by removing the Update Sequence. + * + * Return: + * 0 Everything's cool. + * else Consider this record as damaged. + */ +static BOOL check_file_record(u8 *buffer, u16 buflen) +{ + u16 usa_count, usa_ofs, attrs_offset, usa; + u32 bytes_in_use, bytes_allocated, i; + MFT_RECORD *mft_rec = (MFT_RECORD *)buffer; + ATTR_REC *attr_rec; + + // check record magic + assert_u32_equal(mft_rec->magic, magic_FILE, "FILE record magic"); + // todo: records 16-23 must be filled in order. + // todo: what to do with magic_BAAD? + + // check usa_count+offset to update seq <= attrs_offset < + // bytes_in_use <= bytes_allocated <= buflen. + usa_ofs = le16_to_cpu(mft_rec->usa_ofs); + usa_count = le16_to_cpu(mft_rec->usa_count); + attrs_offset = le16_to_cpu(mft_rec->attrs_offset); + bytes_in_use = le32_to_cpu(mft_rec->bytes_in_use); + bytes_allocated = le32_to_cpu(mft_rec->bytes_allocated); + if (assert_u32_lesseq(usa_ofs+usa_count, attrs_offset, + "usa_ofs+usa_count <= attrs_offset") || + assert_u32_less(attrs_offset, bytes_in_use, + "attrs_offset < bytes_in_use") || + assert_u32_lesseq(bytes_in_use, bytes_allocated, + "bytes_in_use <= bytes_allocated") || + assert_u32_lesseq(bytes_allocated, buflen, + "bytes_allocated <= max_record_size")) { + return 1; + } + + + // We should know all the flags. + if (mft_rec->flags>0xf) { + check_failed("Unknown MFT record flags (0x%x).\n", + (unsigned int)mft_rec->flags); + } + // todo: flag in_use must be on. + + // Remove update seq & check it. + usa = *(u16*)(buffer+usa_ofs); // The value that should be at the end of every sector. + assert_u32_equal(usa_count-1, buflen/NTFS_BLOCK_SIZE, "USA length"); + for (i=1;i<usa_count;i++) { + u16 *fixup = (u16*)(buffer+NTFS_BLOCK_SIZE*i-2); // the value at the end of the sector. + u16 saved_val = *(u16*)(buffer+usa_ofs+2*i); // the actual data value that was saved in the us array. + + assert_u32_equal(*fixup, usa, "fixup"); + *fixup = saved_val; // remove it. + } + + attr_rec = (ATTR_REC *)(buffer + attrs_offset); + while ((u8*)attr_rec<=buffer+buflen-4) { + + // Check attribute record. (Only what is in the buffer) + if (attr_rec->type==AT_END) { + // Done. + return 0; + } + if ((u8*)attr_rec>buffer+buflen-8) { + // not AT_END yet no room for the length field. + check_failed("Attribute 0x%x is not AT_END, yet no " + "room for the length field.\n", + (int)le32_to_cpu(attr_rec->type)); + return 1; + } + + attr_rec = check_attr_record(attr_rec, mft_rec, buflen); + if (!attr_rec) + return 1; + } + // If we got here, there was an overflow. + return 1; + + // todo: an attribute should be at the offset to first attribute, and the offset should be inside the buffer. It should have the value of "next attribute id". + // todo: if base record, it should start with attribute 0x10. + + // Highlevel check of attributes. + // todo: Attributes are well-formed. + // todo: Room for next attribute in the end of the previous record. + + return FALSE; +} + +static void replay_log(ntfs_volume *vol __attribute__((unused))) +{ + // At this time, only check that the log is fully replayed. + ntfs_log_warning("Unsupported: replay_log()\n"); + // todo: if logfile is clean, return success. + unsupported++; +} + +static void verify_mft_record(ntfs_volume *vol, s64 mft_num) +{ + u8 *buffer; + int is_used; + + current_mft_record = mft_num; + + is_used = mft_bitmap_get_bit(mft_num); + if (is_used<0) { + ntfs_log_error("Error getting bit value for record %lld.\n", + (long long)mft_num); + } else if (!is_used) { + ntfs_log_verbose("Record %lld unused. Skipping.\n", + (long long)mft_num); + return; + } + + buffer = ntfs_malloc(vol->mft_record_size); + if (!buffer) + goto verify_mft_record_error; + + ntfs_log_verbose("MFT record %lld\n", (long long)mft_num); + if (ntfs_attr_pread(vol->mft_na, mft_num*vol->mft_record_size, vol->mft_record_size, buffer) < 0) { + ntfs_log_perror("Couldn't read $MFT record %lld", (long long)mft_num); + goto verify_mft_record_error; + } + + check_file_record(buffer, vol->mft_record_size); + // todo: if offset to first attribute >= 0x30, number of mft record should match. + // todo: Match the "record is used" with the mft bitmap. + // todo: if this is not base, check that the parent is a base, and is in use, and pointing to this record. + + // todo: if base record: for each extent record: + // todo: verify_file_record + // todo: hard link count should be the number of 0x30 attributes. + // todo: Order of attributes. + // todo: make sure compression_unit is the same. + + return; +verify_mft_record_error: + + if (buffer) + free(buffer); + errors++; +} + +/** + * This function serves as bootstraping for the more comprehensive checks. + * It will load the MFT runlist and MFT/Bitmap runlist. + * It should not depend on other checks or we may have a circular dependancy. + * Also, this loadng must be forgiving, unlike the comprehensive checks. + */ +static int verify_mft_preliminary(ntfs_volume *rawvol) +{ + current_mft_record = 0; + s64 mft_offset, mftmirr_offset; + int res; + + ntfs_log_trace("Entering verify_mft_preliminary().\n"); + // todo: get size_of_file_record from boot sector + // Load the first segment of the $MFT/DATA runlist. + mft_offset = rawvol->mft_lcn * rawvol->cluster_size; + mftmirr_offset = rawvol->mftmirr_lcn * rawvol->cluster_size; + mft_rl = load_runlist(rawvol, mft_offset, AT_DATA, 1024); + if (!mft_rl) { + check_failed("Loading $MFT runlist failed. Trying $MFTMirr.\n"); + mft_rl = load_runlist(rawvol, mftmirr_offset, AT_DATA, 1024); + } + if (!mft_rl) { + check_failed("Loading $MFTMirr runlist failed too. Aborting.\n"); + return RETURN_FS_ERRORS_LEFT_UNCORRECTED | RETURN_OPERATIONAL_ERROR; + } + // TODO: else { recover $MFT } // Use $MFTMirr to recover $MFT. + // todo: support loading the next runlist extents when ATTRIBUTE_LIST is used on $MFT. + // If attribute list: Gradually load mft runlist. (parse runlist from first file record, check all referenced file records, continue with the next file record). If no attribute list, just load it. + + // Load the runlist of $MFT/Bitmap. + // todo: what about ATTRIBUTE_LIST? Can we reuse code? + mft_bitmap_rl = load_runlist(rawvol, mft_offset, AT_BITMAP, 1024); + if (!mft_bitmap_rl) { + check_failed("Loading $MFT/Bitmap runlist failed. Trying $MFTMirr.\n"); + mft_bitmap_rl = load_runlist(rawvol, mftmirr_offset, AT_BITMAP, 1024); + } + if (!mft_bitmap_rl) { + check_failed("Loading $MFTMirr/Bitmap runlist failed too. Aborting.\n"); + return RETURN_FS_ERRORS_LEFT_UNCORRECTED; + // todo: rebuild the bitmap by using the "in_use" file record flag or by filling it with 1's. + } + + /* Load $MFT/Bitmap */ + if ((res = mft_bitmap_load(rawvol))) + return res; + return -1; /* FIXME: Just added to fix compiler warning without + thinking about what should be here. (Yura) */ +} + +static void check_volume(ntfs_volume *vol) +{ + s64 mft_num, nr_mft_records; + + ntfs_log_warning("Unsupported: check_volume()\n"); + unsupported++; + + // For each mft record, verify that it contains a valid file record. + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + ntfs_log_info("Checking %lld MFT records.\n", (long long)nr_mft_records); + + for (mft_num=0; mft_num < nr_mft_records; mft_num++) { + verify_mft_record(vol, mft_num); + } + + // todo: Check metadata files. + + // todo: Second pass on mft records. Now check the contents as well. + // todo: When going through runlists, build a bitmap. + + // todo: cluster accounting. + return; +} + +static int reset_dirty(ntfs_volume *vol) +{ + u16 flags; + + if (!(vol->flags | VOLUME_IS_DIRTY)) + return 0; + + ntfs_log_verbose("Resetting dirty flag.\n"); + + flags = vol->flags & ~VOLUME_IS_DIRTY; + + if (ntfs_volume_write_flags(vol, flags)) { + ntfs_log_error("Error setting volume flags.\n"); + return -1; + } + return 0; +} + +/** + * main - Does just what C99 claim it does. + * + * For more details on arguments and results, check the man page. + */ +int main(int argc, char **argv) +{ + struct ntfs_device *dev; + ntfs_volume rawvol; + ntfs_volume *vol; + const char *name; + int ret; + + if (argc != 2) + return RETURN_USAGE_OR_SYNTAX_ERROR; + name = argv[1]; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + //ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_PROGRESS); + + /* Allocate an ntfs_device structure. */ + dev = ntfs_device_alloc(name, 0, &ntfs_device_default_io_ops, NULL); + if (!dev) + return RETURN_OPERATIONAL_ERROR; + if (dev->d_ops->open(dev, O_RDONLY)) { //O_RDWR/O_RDONLY? + ntfs_log_perror("Error opening partition device"); + ntfs_device_free(dev); + return RETURN_OPERATIONAL_ERROR; + } + + if ((ret = verify_boot_sector(dev,&rawvol))) { + dev->d_ops->close(dev); + return ret; + } + ntfs_log_verbose("Boot sector verification complete. Proceeding to $MFT"); + + verify_mft_preliminary(&rawvol); + + /* ntfs_device_mount() expects the device to be closed. */ + if (dev->d_ops->close(dev)) + ntfs_log_perror("Failed to close the device."); + + // at this point we know that the volume is valid enough for mounting. + + /* Call ntfs_device_mount() to do the actual mount. */ + vol = ntfs_device_mount(dev, NTFS_MNT_RDONLY); + if (!vol) { + ntfs_device_free(dev); + return 2; + } + + replay_log(vol); + + if (vol->flags & VOLUME_IS_DIRTY) + ntfs_log_warning("Volume is dirty.\n"); + + check_volume(vol); + + if (errors) + ntfs_log_info("Errors found.\n"); + if (unsupported) + ntfs_log_info("Unsupported cases found.\n"); + + if (!errors && !unsupported) { + reset_dirty(vol); + } + + ntfs_umount(vol, FALSE); + + if (errors) + return 2; + if (unsupported) + return 1; + return 0; +} + diff --git a/ntfsprogs/ntfsclone.8 b/ntfsprogs/ntfsclone.8 new file mode 100755 index 0000000000000000000000000000000000000000..9b5c6a3ff448cf734d6fd60d40107f250faf0a5f --- /dev/null +++ b/ntfsprogs/ntfsclone.8 @@ -0,0 +1,392 @@ +.\" Copyright (c) 2003\-2005 Richard Russon. +.\" Copyright (c) 2003\-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2004 Per Olofsson. +.\" Copyright (c) 2010\-2013 Jean-Pierre Andre. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCLONE 8 "February 2013" "ntfs-3g 2015.3.14" +.SH NAME +ntfsclone \- Efficiently clone, image, restore or rescue an NTFS +.SH SYNOPSIS +.B ntfsclone +[\fIOPTIONS\fR] +.I SOURCE +.br +.B ntfsclone \-\-save\-image +[\fIOPTIONS\fR] +.I SOURCE +.br +.B ntfsclone \-\-restore\-image +[\fIOPTIONS\fR] +.I SOURCE +.br +.B ntfsclone \-\-metadata +[\fIOPTIONS\fR] +.I SOURCE +.SH DESCRIPTION +.B ntfsclone +will efficiently clone (copy, save, backup, restore) or rescue an NTFS +filesystem to a sparse file, image, device (partition) or standard output. +It works at disk sector level and +copies only the used data. Unused disk space becomes zero (cloning to +sparse file), encoded with control codes (saving in special image format), +left unchanged (cloning to a disk/partition) or +filled with zeros (cloning to standard output). + +.B ntfsclone +can be useful to make backups, an exact snapshot of an NTFS filesystem +and restore it later on, or for developers to test NTFS read/write +functionality, troubleshoot/investigate users' issues using the clone +without the risk of destroying the original filesystem. + +The clone, if not using the special image format, is an exact copy of the +original NTFS filesystem from sector to sector thus it can be also mounted +just like the original NTFS filesystem. +For example if you clone to a file and the kernel has loopback device and +NTFS support then the file can be mounted as +.RS +.sp +.B mount \-t ntfs \-o loop ntfsclone.img /mnt/ntfsclone +.sp +.RE +.SS Windows Cloning +If you want to copy, move or restore a system or boot partition to another +computer, or to a different disk or partition (e.g. hda1\->hda2, hda1\->hdb1 +or to a different disk sector offset) then you will need to take extra care. + +Usually, Windows will not be able to boot, unless you copy, move or restore +NTFS to the same partition which starts at the same sector on the same type +of disk having the same BIOS legacy cylinder setting as the original +partition and disk had. + +The ntfsclone utility guarantees to make an exact copy of NTFS but it +won't deal with booting issues. This is by design: ntfsclone is a +filesystem, not system utility. Its aim is only NTFS cloning, not Windows +cloning. Hereby ntfsclone can be used as a very fast and reliable +build block for Windows cloning but itself it's not enough. +.SS Sparse Files +A file is sparse if it has unallocated blocks (holes). The reported size of +such files are always higher than the disk space consumed by them. The +.BR du +command can tell the real disk space used by a sparse file. +The holes are always read as zeros. All major Linux filesystem like, +ext2, ext3, reiserfs, Reiser4, JFS and XFS, supports +sparse files but for example the ISO 9600 CD\-ROM filesystem doesn't. +.SS Handling Large Sparse Files +As of today Linux provides inadequate support for managing (tar, +cp, gzip, gunzip, bzip2, bunzip2, cat, etc) large sparse files. +The only main Linux filesystem +having support for efficient sparse file handling is XFS by the +XFS_IOC_GETBMAPX +.BR ioctl (2) . +However none of the common utilities supports it. +This means when you tar, cp, gzip, bzip2, etc a large sparse file +they will always read the entire file, even if you use the "sparse support" +options. + +.BR bzip2 (1) +compresses large sparse files much better than +.BR gzip (1) +but it does so +also much slower. Moreover neither of them handles large sparse +files efficiently during uncompression from disk space usage point +of view. + +At present the most efficient way, both speed and space\-wise, to +compress and uncompress large sparse files by common tools +would be using +.BR tar (1) +with the options +.B \-S +(handle sparse files "efficiently") and +.B \-j +(filter the archive through bzip2). Although +.BR tar +still reads and analyses the entire file, it doesn't pass on the +large data blocks having only zeros to filters and it also avoids +writing large amount of zeros to the disk needlessly. But since +.BR tar +can't create an archive from the standard input, you can't do this +in\-place by just reading +.BR ntfsclone +standard output. Even more sadly, using the \-S option results +serious data loss since the end of 2004 and the GNU +.BR tar +maintainers didn't release fixed versions until the present day. +.SS The Special Image Format +It's also possible, actually it's recommended, to save an NTFS filesystem +to a special image format. +Instead of representing unallocated blocks as holes, they are +encoded using control codes. Thus, the image saves space without +requiring sparse file support. The image format is ideal for streaming +filesystem images over the network and similar, and can be used as a +replacement for Ghost or Partition Image if it is combined with other +tools. The downside is that you can't mount the image directly, you +need to restore it first. + +To save an image using the special image format, use the +.B \-s +or the +.B \-\-save\-image +option. To restore an image, use the +.B \-r +or the +.B \-\-restore\-image +option. Note that you can restore images from standard input by +using '\-' as the +.I SOURCE +file. +.SS Metadata\-only Cloning +One of the features of +.BR ntfsclone +is that, it can also save only the NTFS metadata using the option +.B \-m +or +.B \-\-metadata +and the clone still will be +mountable. In this case all non\-metadata file content will be lost and +reading them back will result always zeros. + +The metadata\-only image can be compressed very +well, usually to not more than 1\-8 MB thus it's easy to transfer +for investigation, troubleshooting. + +In this mode of ntfsclone, +.B NONE +of the user's data is saved, including the resident user's data +embedded into metadata. All is filled with zeros. +Moreover all the file timestamps, deleted and unused spaces inside +the metadata are filled with zeros. Thus this mode is inappropriate +for example for forensic analyses. +This mode may be combined with \fB\-\-save\-image\fP to create a +special image format file instead of a sparse file. + +Please note, filenames are not wiped out. They might contain +sensitive information, so think twice before sending such an +image to anybody. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsclone +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.B \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.B "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-o\fR, \fB\-\-output\fR FILE +Clone NTFS to the non\-existent +.IR FILE . +If +.I FILE +is '\-' then clone to the +standard output. +.TP +\fB\-O\fR, \fB\-\-overwrite\fR FILE +Clone NTFS to +.IR FILE , +overwriting if exists. +.TP +\fB\-s\fR, \fB\-\-save\-image\fR +Save to the special image format. This is the most efficient way space and +speed\-wise if imaging is done to the standard output, e.g. for image +compression, encryption or streaming through a network. +.TP +\fB\-r\fR, \fB\-\-restore\-image\fR +Restore from the special image format specified by +.I SOURCE +argument. If the +.I SOURCE +is '\-' then the image is read from the standard input. +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Test the consistency of a saved image by simulating its restoring without +writing anything. The NTFS data contained in the image is not tested. +The option \fB\-\-restore\-image\fR must also be present, and the options +\fB\-\-output\fR and \fB\-\-overwrite\fR must be omitted. +.TP +\fB\-\-rescue\fR +Ignore disk read errors so disks having bad sectors, e.g. dying disks, can be +rescued the most efficiently way, with minimal stress on them. Ntfsclone works +at the lowest, sector level in this mode too thus more data can be rescued. +The contents of the unreadable sectors are filled by character '?' and the +beginning of such sectors are marked by "BadSectoR\\0". +.TP +\fB\-m\fR, \fB\-\-metadata\fR +Clone +.B ONLY METADATA +(for NTFS experts). Only cloning to a (sparse) file is allowed, unless used +the option \fB\-\-save\-image\fP is also used. +You can't metadata\-only clone to a device. +.TP +\fB\-\-ignore\-fs\-check\fR +Ignore the result of the filesystem consistency check. This option is allowed +to be used only with the +.B \-\-metadata +option, for the safety of user's data. The clusters which cause the +inconsistency are saved too. +.TP +\fB\-t\fR, \fB\-\-preserve\-timestamps\fR +Do not wipe the timestamps, to be used only with the +.B \-\-metadata +option. + + +.TP +\fB\-\-new\-serial\fR, or +.TP +\fB\-\-new\-half\-serial\fR +Set a new random serial number to the clone. The serial number is a 64 +bit number used to identify the device during the mounting process, so +it has to be changed to enable the original file system +and the clone to be mounted at the same time on the same computer. + +The option \fB\-\-new\-half\-serial\fP only changes the upper part of the +serial number, keeping the lower part which is used by Windows unchanged. + +The options \fB\-\-new\-serial\fP and \fB\-\-new\-half\-serial\fP can +only be used when cloning a file system of restoring from an image. + +The serial number is not the volume UUID used by Windows +to locate files which have been moved to another volume. + +.TP +\fB\-f\fR, \fB\-\-force\fR +Forces ntfsclone to proceed if the filesystem is marked +"dirty" for consistency check. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Do not display any progress-bars during operation. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.SH EXIT CODES +The exit code is 0 on success, non\-zero otherwise. +.SH EXAMPLES +Clone NTFS on /dev/hda1 to /dev/hdc1: +.RS +.sp +.B ntfsclone \-\-overwrite /dev/hdc1 /dev/hda1 +.sp +.RE +Save an NTFS to a file in the special image format: +.RS +.sp +.B ntfsclone \-\-save\-image \-\-output backup.img /dev/hda1 +.sp +.RE +Restore an NTFS from a special image file to its original partition: +.RS +.sp +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 backup.img +.sp +.RE +Save an NTFS into a compressed image file: +.RS +.sp +.B ntfsclone \-\-save\-image \-o \- /dev/hda1 | gzip \-c > backup.img.gz +.sp +.RE +Restore an NTFS volume from a compressed image file: +.RS +.sp +.B gunzip \-c backup.img.gz | \\\\ +.br +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 \- +.sp +.RE +Backup an NTFS volume to a remote host, using ssh. Please note, that +ssh may ask for a password! +.RS +.sp +.B ntfsclone \-\-save\-image \-\-output \- /dev/hda1 | \\\\ +.br +.B gzip \-c | ssh host 'cat > backup.img.gz' +.sp +.RE +Restore an NTFS volume from a remote host via ssh. Please note, that +ssh may ask for a password! +.RS +.sp +.B ssh host 'cat backup.img.gz' | gunzip \-c | \\\\ +.br +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 \- +.sp +.RE +Stream an image file from a web server and restore it to a partition: +.RS +.sp +.B wget \-qO \- http://server/backup.img | \\\\ +.br +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 \- +.sp +.RE +Clone an NTFS volume to a non\-existent file: +.RS +.sp +.B ntfsclone \-\-output ntfs\-clone.img /dev/hda1 +.sp +.RE +Pack NTFS metadata for NTFS experts. Please note that bzip2 runs +very long but results usually at least 10 times smaller archives +than gzip on a sparse file. +.RS +.sp +.B ntfsclone \-\-metadata \-\-output ntfsmeta.img /dev/hda1 +.br +.B bzip2 ntfsmeta.img +.sp +Or, outputting to a compressed image : +.br +.B ntfsclone \-mst \-\-output - /dev/hda1 | bzip2 > ntfsmeta.bz2 +.sp +.RE +Unpacking NTFS metadata into a sparse file: +.RS +.sp +.B bunzip2 \-c ntfsmeta.img.bz2 | \\\\ +.br +.B cp \-\-sparse=always /proc/self/fd/0 ntfsmeta.img +.sp +.RE +.SH KNOWN ISSUES +There are no known problems with +.BR ntfsclone . +If you think you have found a problem then please send an email describing it +to the development team: +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.sp +Sometimes it might appear ntfsclone froze if the clone is on ReiserFS +and even CTRL\-C won't stop it. This is not a bug in ntfsclone, however +it's due to ReiserFS being extremely inefficient creating large +sparse files and not handling signals during this operation. This +ReiserFS problem was improved in kernel 2.4.22. +XFS, JFS and ext3 don't have this problem. +.hy +.SH AUTHORS +.B ntfsclone +was written by Szabolcs Szakacsits with contributions from Per Olofsson +(special image format support) and Anton Altaparmakov. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsclone +is part of the +.B ntfs-3g +package and is available at: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsresize (8) +.BR ntfsprogs (8) +.BR xfs_copy (8) +.BR debugreiserfs (8) +.BR e2image (8) diff --git a/ntfsprogs/ntfsclone.8.in b/ntfsprogs/ntfsclone.8.in new file mode 100755 index 0000000000000000000000000000000000000000..d156634cd6897e9f05782dea7698dd165438e6e0 --- /dev/null +++ b/ntfsprogs/ntfsclone.8.in @@ -0,0 +1,392 @@ +.\" Copyright (c) 2003\-2005 Richard Russon. +.\" Copyright (c) 2003\-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2004 Per Olofsson. +.\" Copyright (c) 2010\-2013 Jean-Pierre Andre. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCLONE 8 "February 2013" "ntfs-3g @VERSION@" +.SH NAME +ntfsclone \- Efficiently clone, image, restore or rescue an NTFS +.SH SYNOPSIS +.B ntfsclone +[\fIOPTIONS\fR] +.I SOURCE +.br +.B ntfsclone \-\-save\-image +[\fIOPTIONS\fR] +.I SOURCE +.br +.B ntfsclone \-\-restore\-image +[\fIOPTIONS\fR] +.I SOURCE +.br +.B ntfsclone \-\-metadata +[\fIOPTIONS\fR] +.I SOURCE +.SH DESCRIPTION +.B ntfsclone +will efficiently clone (copy, save, backup, restore) or rescue an NTFS +filesystem to a sparse file, image, device (partition) or standard output. +It works at disk sector level and +copies only the used data. Unused disk space becomes zero (cloning to +sparse file), encoded with control codes (saving in special image format), +left unchanged (cloning to a disk/partition) or +filled with zeros (cloning to standard output). + +.B ntfsclone +can be useful to make backups, an exact snapshot of an NTFS filesystem +and restore it later on, or for developers to test NTFS read/write +functionality, troubleshoot/investigate users' issues using the clone +without the risk of destroying the original filesystem. + +The clone, if not using the special image format, is an exact copy of the +original NTFS filesystem from sector to sector thus it can be also mounted +just like the original NTFS filesystem. +For example if you clone to a file and the kernel has loopback device and +NTFS support then the file can be mounted as +.RS +.sp +.B mount \-t ntfs \-o loop ntfsclone.img /mnt/ntfsclone +.sp +.RE +.SS Windows Cloning +If you want to copy, move or restore a system or boot partition to another +computer, or to a different disk or partition (e.g. hda1\->hda2, hda1\->hdb1 +or to a different disk sector offset) then you will need to take extra care. + +Usually, Windows will not be able to boot, unless you copy, move or restore +NTFS to the same partition which starts at the same sector on the same type +of disk having the same BIOS legacy cylinder setting as the original +partition and disk had. + +The ntfsclone utility guarantees to make an exact copy of NTFS but it +won't deal with booting issues. This is by design: ntfsclone is a +filesystem, not system utility. Its aim is only NTFS cloning, not Windows +cloning. Hereby ntfsclone can be used as a very fast and reliable +build block for Windows cloning but itself it's not enough. +.SS Sparse Files +A file is sparse if it has unallocated blocks (holes). The reported size of +such files are always higher than the disk space consumed by them. The +.BR du +command can tell the real disk space used by a sparse file. +The holes are always read as zeros. All major Linux filesystem like, +ext2, ext3, reiserfs, Reiser4, JFS and XFS, supports +sparse files but for example the ISO 9600 CD\-ROM filesystem doesn't. +.SS Handling Large Sparse Files +As of today Linux provides inadequate support for managing (tar, +cp, gzip, gunzip, bzip2, bunzip2, cat, etc) large sparse files. +The only main Linux filesystem +having support for efficient sparse file handling is XFS by the +XFS_IOC_GETBMAPX +.BR ioctl (2) . +However none of the common utilities supports it. +This means when you tar, cp, gzip, bzip2, etc a large sparse file +they will always read the entire file, even if you use the "sparse support" +options. + +.BR bzip2 (1) +compresses large sparse files much better than +.BR gzip (1) +but it does so +also much slower. Moreover neither of them handles large sparse +files efficiently during uncompression from disk space usage point +of view. + +At present the most efficient way, both speed and space\-wise, to +compress and uncompress large sparse files by common tools +would be using +.BR tar (1) +with the options +.B \-S +(handle sparse files "efficiently") and +.B \-j +(filter the archive through bzip2). Although +.BR tar +still reads and analyses the entire file, it doesn't pass on the +large data blocks having only zeros to filters and it also avoids +writing large amount of zeros to the disk needlessly. But since +.BR tar +can't create an archive from the standard input, you can't do this +in\-place by just reading +.BR ntfsclone +standard output. Even more sadly, using the \-S option results +serious data loss since the end of 2004 and the GNU +.BR tar +maintainers didn't release fixed versions until the present day. +.SS The Special Image Format +It's also possible, actually it's recommended, to save an NTFS filesystem +to a special image format. +Instead of representing unallocated blocks as holes, they are +encoded using control codes. Thus, the image saves space without +requiring sparse file support. The image format is ideal for streaming +filesystem images over the network and similar, and can be used as a +replacement for Ghost or Partition Image if it is combined with other +tools. The downside is that you can't mount the image directly, you +need to restore it first. + +To save an image using the special image format, use the +.B \-s +or the +.B \-\-save\-image +option. To restore an image, use the +.B \-r +or the +.B \-\-restore\-image +option. Note that you can restore images from standard input by +using '\-' as the +.I SOURCE +file. +.SS Metadata\-only Cloning +One of the features of +.BR ntfsclone +is that, it can also save only the NTFS metadata using the option +.B \-m +or +.B \-\-metadata +and the clone still will be +mountable. In this case all non\-metadata file content will be lost and +reading them back will result always zeros. + +The metadata\-only image can be compressed very +well, usually to not more than 1\-8 MB thus it's easy to transfer +for investigation, troubleshooting. + +In this mode of ntfsclone, +.B NONE +of the user's data is saved, including the resident user's data +embedded into metadata. All is filled with zeros. +Moreover all the file timestamps, deleted and unused spaces inside +the metadata are filled with zeros. Thus this mode is inappropriate +for example for forensic analyses. +This mode may be combined with \fB\-\-save\-image\fP to create a +special image format file instead of a sparse file. + +Please note, filenames are not wiped out. They might contain +sensitive information, so think twice before sending such an +image to anybody. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsclone +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.B \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.B "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-o\fR, \fB\-\-output\fR FILE +Clone NTFS to the non\-existent +.IR FILE . +If +.I FILE +is '\-' then clone to the +standard output. +.TP +\fB\-O\fR, \fB\-\-overwrite\fR FILE +Clone NTFS to +.IR FILE , +overwriting if exists. +.TP +\fB\-s\fR, \fB\-\-save\-image\fR +Save to the special image format. This is the most efficient way space and +speed\-wise if imaging is done to the standard output, e.g. for image +compression, encryption or streaming through a network. +.TP +\fB\-r\fR, \fB\-\-restore\-image\fR +Restore from the special image format specified by +.I SOURCE +argument. If the +.I SOURCE +is '\-' then the image is read from the standard input. +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Test the consistency of a saved image by simulating its restoring without +writing anything. The NTFS data contained in the image is not tested. +The option \fB\-\-restore\-image\fR must also be present, and the options +\fB\-\-output\fR and \fB\-\-overwrite\fR must be omitted. +.TP +\fB\-\-rescue\fR +Ignore disk read errors so disks having bad sectors, e.g. dying disks, can be +rescued the most efficiently way, with minimal stress on them. Ntfsclone works +at the lowest, sector level in this mode too thus more data can be rescued. +The contents of the unreadable sectors are filled by character '?' and the +beginning of such sectors are marked by "BadSectoR\\0". +.TP +\fB\-m\fR, \fB\-\-metadata\fR +Clone +.B ONLY METADATA +(for NTFS experts). Only cloning to a (sparse) file is allowed, unless used +the option \fB\-\-save\-image\fP is also used. +You can't metadata\-only clone to a device. +.TP +\fB\-\-ignore\-fs\-check\fR +Ignore the result of the filesystem consistency check. This option is allowed +to be used only with the +.B \-\-metadata +option, for the safety of user's data. The clusters which cause the +inconsistency are saved too. +.TP +\fB\-t\fR, \fB\-\-preserve\-timestamps\fR +Do not wipe the timestamps, to be used only with the +.B \-\-metadata +option. + + +.TP +\fB\-\-new\-serial\fR, or +.TP +\fB\-\-new\-half\-serial\fR +Set a new random serial number to the clone. The serial number is a 64 +bit number used to identify the device during the mounting process, so +it has to be changed to enable the original file system +and the clone to be mounted at the same time on the same computer. + +The option \fB\-\-new\-half\-serial\fP only changes the upper part of the +serial number, keeping the lower part which is used by Windows unchanged. + +The options \fB\-\-new\-serial\fP and \fB\-\-new\-half\-serial\fP can +only be used when cloning a file system of restoring from an image. + +The serial number is not the volume UUID used by Windows +to locate files which have been moved to another volume. + +.TP +\fB\-f\fR, \fB\-\-force\fR +Forces ntfsclone to proceed if the filesystem is marked +"dirty" for consistency check. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Do not display any progress-bars during operation. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.SH EXIT CODES +The exit code is 0 on success, non\-zero otherwise. +.SH EXAMPLES +Clone NTFS on /dev/hda1 to /dev/hdc1: +.RS +.sp +.B ntfsclone \-\-overwrite /dev/hdc1 /dev/hda1 +.sp +.RE +Save an NTFS to a file in the special image format: +.RS +.sp +.B ntfsclone \-\-save\-image \-\-output backup.img /dev/hda1 +.sp +.RE +Restore an NTFS from a special image file to its original partition: +.RS +.sp +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 backup.img +.sp +.RE +Save an NTFS into a compressed image file: +.RS +.sp +.B ntfsclone \-\-save\-image \-o \- /dev/hda1 | gzip \-c > backup.img.gz +.sp +.RE +Restore an NTFS volume from a compressed image file: +.RS +.sp +.B gunzip \-c backup.img.gz | \\\\ +.br +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 \- +.sp +.RE +Backup an NTFS volume to a remote host, using ssh. Please note, that +ssh may ask for a password! +.RS +.sp +.B ntfsclone \-\-save\-image \-\-output \- /dev/hda1 | \\\\ +.br +.B gzip \-c | ssh host 'cat > backup.img.gz' +.sp +.RE +Restore an NTFS volume from a remote host via ssh. Please note, that +ssh may ask for a password! +.RS +.sp +.B ssh host 'cat backup.img.gz' | gunzip \-c | \\\\ +.br +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 \- +.sp +.RE +Stream an image file from a web server and restore it to a partition: +.RS +.sp +.B wget \-qO \- http://server/backup.img | \\\\ +.br +.B ntfsclone \-\-restore\-image \-\-overwrite /dev/hda1 \- +.sp +.RE +Clone an NTFS volume to a non\-existent file: +.RS +.sp +.B ntfsclone \-\-output ntfs\-clone.img /dev/hda1 +.sp +.RE +Pack NTFS metadata for NTFS experts. Please note that bzip2 runs +very long but results usually at least 10 times smaller archives +than gzip on a sparse file. +.RS +.sp +.B ntfsclone \-\-metadata \-\-output ntfsmeta.img /dev/hda1 +.br +.B bzip2 ntfsmeta.img +.sp +Or, outputting to a compressed image : +.br +.B ntfsclone \-mst \-\-output - /dev/hda1 | bzip2 > ntfsmeta.bz2 +.sp +.RE +Unpacking NTFS metadata into a sparse file: +.RS +.sp +.B bunzip2 \-c ntfsmeta.img.bz2 | \\\\ +.br +.B cp \-\-sparse=always /proc/self/fd/0 ntfsmeta.img +.sp +.RE +.SH KNOWN ISSUES +There are no known problems with +.BR ntfsclone . +If you think you have found a problem then please send an email describing it +to the development team: +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.sp +Sometimes it might appear ntfsclone froze if the clone is on ReiserFS +and even CTRL\-C won't stop it. This is not a bug in ntfsclone, however +it's due to ReiserFS being extremely inefficient creating large +sparse files and not handling signals during this operation. This +ReiserFS problem was improved in kernel 2.4.22. +XFS, JFS and ext3 don't have this problem. +.hy +.SH AUTHORS +.B ntfsclone +was written by Szabolcs Szakacsits with contributions from Per Olofsson +(special image format support) and Anton Altaparmakov. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsclone +is part of the +.B ntfs-3g +package and is available at: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsresize (8) +.BR ntfsprogs (8) +.BR xfs_copy (8) +.BR debugreiserfs (8) +.BR e2image (8) diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c new file mode 100755 index 0000000000000000000000000000000000000000..1504c252a46e91d6d56a56d87b6499e9eedc8754 --- /dev/null +++ b/ntfsprogs/ntfsclone.c @@ -0,0 +1,2746 @@ +/** + * ntfsclone - Part of the Linux-NTFS project. + * + * Copyright (c) 2003-2006 Szabolcs Szakacsits + * Copyright (c) 2004-2006 Anton Altaparmakov + * Copyright (c) 2010-2014 Jean-Pierre Andre + * Special image format support copyright (c) 2004 Per Olofsson + * + * Clone NTFS data and/or metadata to a sparse file, image, device or stdout. + * + * 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. + */ + +#include "config.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_VFS_H +#include <sys/vfs.h> +#endif +#ifdef HAVE_SYS_STATVFS_H +#include <sys/statvfs.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_MOUNT_H +#include <sys/mount.h> +#endif + +/* + * FIXME: ntfsclone do bad things about endians handling. Fix it and remove + * this note and define. + */ +#define NTFS_DO_NOT_CHECK_ENDIANS + +#include "debug.h" +#include "types.h" +#include "support.h" +#include "endians.h" +#include "bootsect.h" +#include "device.h" +#include "attrib.h" +#include "mst.h" +#include "volume.h" +#include "mft.h" +#include "bitmap.h" +#include "inode.h" +#include "index.h" +#include "dir.h" +#include "runlist.h" +#include "ntfstime.h" +#include "utils.h" +/* #include "version.h" */ +#include "misc.h" + +#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE) +#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */ +#endif +#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64) +#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */ +#endif + +#if defined(linux) || defined(__uClinux__) || defined(__sun) \ + || defined(__APPLE__) || defined(__DARWIN__) + /* Make sure the presence of <windows.h> means compiling for Windows */ +#undef HAVE_WINDOWS_H +#endif + +#if defined(__sun) | defined(HAVE_WINDOWS_H) +#define NO_STATFS 1 /* statfs(2) and f_type are not universal */ +#endif + +#ifdef HAVE_WINDOWS_H +/* + * Replacements for functions which do not exist on Windows + */ +int setmode(int, int); /* from msvcrt.dll */ + +#define getpid() (0) +#define srandom(seed) srand(seed) +#define random() rand() +#define fsync(fd) (0) +#define ioctl(fd,code,buf) (-1) +#define ftruncate(fd, size) ntfs_device_win32_ftruncate(dev_out, size) +#define BINWMODE "wb" +#else +#define BINWMODE "w" +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +static const char *EXEC_NAME = "ntfsclone"; + +static const char *bad_sectors_warning_msg = +"*************************************************************************\n" +"* WARNING: The disk has one or more bad sectors. This means that damage *\n" +"* has occurred on the disk surface, possibly caused by deterioration of *\n" +"* the physical media, manufacturing faults or other reasons. The *\n" +"* reliability of the disk may stay stable or degrade fast. *\n" +"* Use the --rescue option to efficiently save as much data as possible! *\n" +"*************************************************************************\n"; + +static const char *dirty_volume_msg = +"Volume '%s' is scheduled for a check or it was shutdown \n" +"uncleanly. Please boot Windows or use the --force option to progress.\n"; + +static struct { + int verbose; + int quiet; + int debug; + int force; + int overwrite; + int std_out; + int blkdev_out; /* output file is block device */ + int metadata; /* metadata only cloning */ + int no_action; /* do not really restore */ + int ignore_fs_check; + int rescue; + int save_image; + int new_serial; + int metadata_image; + int preserve_timestamps; + int restore_image; + char *output; + char *volume; +#ifndef NO_STATFS + struct statfs stfs; +#endif +} opt; + +struct bitmap { + s64 size; + u8 *bm; +}; + +struct progress_bar { + u64 start; + u64 stop; + int resolution; + float unit; +}; + +typedef struct { + ntfs_inode *ni; /* inode being processed */ + ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ + s64 inuse; /* number of clusters in use */ + int more_use; /* possibly allocated clusters */ + LCN current_lcn; +} ntfs_walk_clusters_ctx; + +typedef int (ntfs_walk_op)(ntfs_inode *ni, void *data); + +struct ntfs_walk_cluster { + ntfs_walk_op *inode_op; /* not implemented yet */ + ntfs_walk_clusters_ctx *image; +}; + + +static ntfs_volume *vol = NULL; +static struct bitmap lcn_bitmap; + +static int fd_in; +static int fd_out; +static FILE *stream_out = (FILE*)NULL; +struct ntfs_device *dev_out = (struct ntfs_device*)NULL; +static FILE *msg_out = NULL; + +static int wipe = 0; +static unsigned int nr_used_mft_records = 0; +static unsigned int wiped_unused_mft_data = 0; +static unsigned int wiped_unused_mft = 0; +static unsigned int wiped_resident_data = 0; +static unsigned int wiped_timestamp_data = 0; + +static le64 volume_serial_number; /* new random serial number */ +static u64 full_device_size; /* full size, including the backup boot sector */ + +static BOOL image_is_host_endian = FALSE; + +#define IMAGE_MAGIC "\0ntfsclone-image" +#define IMAGE_MAGIC_SIZE 16 +#define IMAGE_OFFSET_OFFSET 46 /* must be the same for all versions ! */ +#define IMAGE_HDR_ALIGN 8 /* alignment wanted after header */ + +/* This is the first endianness safe format version. */ +#define NTFSCLONE_IMG_VER_MAJOR_ENDIANNESS_SAFE 10 +#define NTFSCLONE_IMG_VER_MINOR_ENDIANNESS_SAFE 0 + +/* + * Set the version to 10.0 to avoid colisions with old ntfsclone which + * stupidly used the volume version as the image version... )-: I hope NTFS + * never reaches version 10.0 and if it does one day I hope no-one is using + * such an old ntfsclone by then... + * + * NOTE: Only bump the minor version if the image format and header are still + * backwards compatible. Otherwise always bump the major version. If in + * doubt, bump the major version. + * + * Moved to 10.1 : Alternate boot sector now saved. Still compatible. + */ +#define NTFSCLONE_IMG_VER_MAJOR 10 +#define NTFSCLONE_IMG_VER_MINOR 1 + +enum { CMD_GAP, CMD_NEXT } ; + +/* All values are in little endian. */ +static struct image_hdr { + char magic[IMAGE_MAGIC_SIZE]; + u8 major_ver; + u8 minor_ver; + /* the following is aligned dangerously (too late...) */ + le32 cluster_size; + le64 device_size; + sle64 nr_clusters; + le64 inuse; + le32 offset_to_image_data; /* From start of image_hdr. */ +} __attribute__((__packed__)) image_hdr; + +static int compare_bitmaps(struct bitmap *a, BOOL copy); + +#define NTFSCLONE_IMG_HEADER_SIZE_OLD \ + (offsetof(struct image_hdr, offset_to_image_data)) + +#define NTFS_MBYTE (1000 * 1000) + +#define ERR_PREFIX "ERROR" +#define PERR_PREFIX ERR_PREFIX "(%d): " +#define NERR_PREFIX ERR_PREFIX ": " + +#define LAST_METADATA_INODE 11 + +#define NTFS_MAX_CLUSTER_SIZE 65536 +#define NTFS_SECTOR_SIZE 512 + +#define rounded_up_division(a, b) (((a) + (b - 1)) / (b)) + +#define read_all(f, p, n) io_all((f), (p), (n), 0) +#define write_all(f, p, n) io_all((f), (p), (n), 1) + +__attribute__((format(printf, 1, 2))) +static void Printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(msg_out, fmt, ap); + va_end(ap); + fflush(msg_out); +} + +__attribute__((format(printf, 1, 2))) +static void perr_printf(const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + Printf(PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(msg_out, fmt, ap); + va_end(ap); + Printf(": %s\n", strerror(eo)); + fflush(msg_out); +} + +__attribute__((format(printf, 1, 2))) +static void err_printf(const char *fmt, ...) +{ + va_list ap; + + Printf(NERR_PREFIX); + va_start(ap, fmt); + vfprintf(msg_out, fmt, ap); + va_end(ap); + fflush(msg_out); +} + +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static void err_exit(const char *fmt, ...) +{ + va_list ap; + + Printf(NERR_PREFIX); + va_start(ap, fmt); + vfprintf(msg_out, fmt, ap); + va_end(ap); + fflush(msg_out); + if (vol) + ntfs_umount(vol,FALSE); + exit(1); +} + +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static void perr_exit(const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + Printf(PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(msg_out, fmt, ap); + va_end(ap); + Printf(": %s\n", strerror(eo)); + fflush(msg_out); + if (vol) + ntfs_umount(vol,FALSE); + exit(1); +} + + +__attribute__((noreturn)) +static void usage(int ret) +{ + fprintf(stderr, "\nUsage: %s [OPTIONS] SOURCE\n" + " Efficiently clone NTFS to a sparse file, image, device or standard output.\n" + "\n" + " -o, --output FILE Clone NTFS to the non-existent FILE\n" + " -O, --overwrite FILE Clone NTFS to FILE, overwriting if exists\n" + " -s, --save-image Save to the special image format\n" + " -r, --restore-image Restore from the special image format\n" + " --rescue Continue after disk read errors\n" + " -m, --metadata Clone *only* metadata (for NTFS experts)\n" + " -n, --no-action Test restoring, without outputting anything\n" + " --ignore-fs-check Ignore the filesystem check result\n" + " --new-serial Set a new serial number\n" + " --new-half-serial Set a partial new serial number\n" + " -t, --preserve-timestamps Do not clear the timestamps\n" + " -q, --quiet Do not display any progress bars\n" + " -f, --force Force to progress (DANGEROUS)\n" + " -h, --help Display this help\n" +#ifdef DEBUG + " -d, --debug Show debug information\n" +#endif + " -V, --version Display version information\n" + "\n" + " If FILE is '-' then send the image to the standard output. If SOURCE is '-'\n" + " and --restore-image is used then read the image from the standard input.\n" + "\n", EXEC_NAME); + fprintf(stderr, "%s%s", ntfs_bugs, ntfs_home); + exit(ret); +} + +/** + * version + */ +__attribute__((noreturn)) +static void version(void) +{ + fprintf(stderr, + "Efficiently clone, image, restore or rescue an NTFS Volume.\n\n" + "Copyright (c) 2003-2006 Szabolcs Szakacsits\n" + "Copyright (c) 2004-2006 Anton Altaparmakov\n" + "Copyright (c) 2010-2014 Jean-Pierre Andre\n\n"); + fprintf(stderr, "%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); + exit(0); +} + +static void parse_options(int argc, char **argv) +{ + static const char *sopt = "-dfhmno:O:qrstV"; + static const struct option lopt[] = { +#ifdef DEBUG + { "debug", no_argument, NULL, 'd' }, +#endif + { "quiet", no_argument, NULL, 'q' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "metadata", no_argument, NULL, 'm' }, + { "no-action", no_argument, NULL, 'n' }, + { "output", required_argument, NULL, 'o' }, + { "overwrite", required_argument, NULL, 'O' }, + { "restore-image", no_argument, NULL, 'r' }, + { "ignore-fs-check", no_argument, NULL, 'C' }, + { "rescue", no_argument, NULL, 'R' }, + { "new-serial", no_argument, NULL, 'I' }, + { "new-half-serial", no_argument, NULL, 'i' }, + { "save-image", no_argument, NULL, 's' }, + { "preserve-timestamps", no_argument, NULL, 't' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + memset(&opt, 0, sizeof(opt)); + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (opt.volume) + usage(1); + opt.volume = argv[optind-1]; + break; + case 'd': + opt.debug++; + break; + case 'q': + opt.quiet++; + break; + case 'f': + opt.force++; + break; + case 'h': + usage(0); + case '?': + usage(1); + case 'i': /* not proposed as a short option */ + opt.new_serial |= 1; + break; + case 'I': /* not proposed as a short option */ + opt.new_serial |= 2; + break; + case 'm': + opt.metadata++; + break; + case 'n': + opt.no_action++; + break; + case 'O': + opt.overwrite++; + case 'o': + if (opt.output) + usage(1); + opt.output = optarg; + break; + case 'r': + opt.restore_image++; + break; + case 'C': + opt.ignore_fs_check++; + break; + case 'R': + opt.rescue++; + break; + case 's': + opt.save_image++; + break; + case 't': + opt.preserve_timestamps++; + break; + case 'V': + version(); + break; + default: + err_printf("Unknown option '%s'.\n", argv[optind-1]); + usage(1); + } + } + + if (!opt.no_action && (opt.output == NULL)) { + err_printf("You must specify an output file.\n"); + usage(1); + } + + if (!opt.no_action && (strcmp(opt.output, "-") == 0)) + opt.std_out++; + + if (opt.volume == NULL) { + err_printf("You must specify a device file.\n"); + usage(1); + } + + if (!opt.restore_image && !strcmp(opt.volume, "-")) { + err_printf("Only special images can be read from standard input\n"); + usage(1); + } + + if (opt.metadata && opt.save_image) { + opt.metadata_image++; + opt.save_image = 0; + } + + if (opt.metadata && opt.restore_image) + err_exit("Restoring only metadata from an image is not " + "supported!\n"); + + if (opt.metadata && !opt.metadata_image && opt.std_out) + err_exit("Cloning only metadata to stdout isn't supported!\n"); + + if (opt.ignore_fs_check && !opt.metadata && !opt.rescue) + err_exit("Filesystem check can be ignored only for metadata " + "cloning or rescue situations!\n"); + + if (opt.save_image && opt.restore_image) + err_exit("Saving and restoring an image at the same time " + "is not supported!\n"); + + if (opt.no_action && !opt.restore_image) + err_exit("A restoring test requires the restore option!\n"); + + if (opt.no_action && opt.output) + err_exit("A restoring test requires not defining any output!\n"); + + if (!opt.no_action && !opt.std_out) { + struct stat st; +#ifdef HAVE_WINDOWS_H + BOOL blkdev = opt.output[0] && (opt.output[1] == ':') + && !opt.output[2]; + + if (!blkdev && (stat(opt.output, &st) == -1)) { +#else + if (stat(opt.output, &st) == -1) { +#endif + if (errno != ENOENT) + perr_exit("Couldn't access '%s'", opt.output); + } else { + if (!opt.overwrite) + err_exit("Output file '%s' already exists.\n" + "Use option --overwrite if you want to" + " replace its content.\n", opt.output); + +#ifdef HAVE_WINDOWS_H + if (blkdev) { +#else + if (S_ISBLK(st.st_mode)) { +#endif + opt.blkdev_out = 1; + if (opt.metadata && !opt.force) + err_exit("Cloning only metadata to a " + "block device does not usually " + "make sense, aborting...\n" + "If you were instructed to do " + "this by a developer and/or are " + "sure that this is what you want " + "to do, run this utility again " + "but this time add the force " + "option, i.e. add '--force' to " + "the command line arguments."); + } + } + } + + /* + * Send messages, debug information and library messages to stdout, + * but, if outputing to stdout send them to stderr + */ + if (opt.std_out) { + msg_out = stderr; + ntfs_log_set_handler(ntfs_log_handler_stderr); + } else { + msg_out = stdout; + ntfs_log_set_handler(ntfs_log_handler_outerr); + } +} + +/* + * Initialize the random number generator with the current + * time, and generate a 64-bit random number for the serial + * number + */ +static void generate_serial_number(void) { + u64 sn; + + /* different values for parallel processes */ + srandom(time((time_t*)NULL) ^ (getpid() << 16)); + sn = ((u64)random() << 32) | ((u64)random() & 0xffffffff); + volume_serial_number = cpu_to_le64(sn); +} + +static void progress_init(struct progress_bar *p, u64 start, u64 stop, int res) +{ + p->start = start; + p->stop = stop; + p->unit = 100.0 / (stop - start); + p->resolution = res; +} + + +static void progress_update(struct progress_bar *p, u64 current) +{ + float percent = p->unit * current; + + if (opt.quiet) + return; + + if (current != p->stop) { + if ((current - p->start) % p->resolution) + return; + Printf("%6.2f percent completed\r", percent); + } else + Printf("100.00 percent completed\n"); + fflush(msg_out); +} + +static s64 is_critical_metadata(ntfs_walk_clusters_ctx *image, runlist *rl) +{ + s64 inode = image->ni->mft_no; + + if (inode <= LAST_METADATA_INODE) { + + /* Don't save bad sectors (both $Bad and unnamed are ignored */ + if (inode == FILE_BadClus && image->ctx->attr->type == AT_DATA) + return 0; + + if (inode != FILE_LogFile) + return rl->length; + + if (image->ctx->attr->type == AT_DATA) { + + /* Save at least the first 16 KiB of FILE_LogFile */ + s64 s = (s64)16384 - rl->vcn * vol->cluster_size; + if (s > 0) { + s = rounded_up_division(s, vol->cluster_size); + if (rl->length < s) + s = rl->length; + return s; + } + return 0; + } + } + + if (image->ctx->attr->type != AT_DATA) + return rl->length; + + return 0; +} + +static off_t tellin(int in) +{ + return (lseek(in, 0, SEEK_CUR)); +} + +static int io_all(void *fd, void *buf, int count, int do_write) +{ + int i; + struct ntfs_device *dev = fd; + + while (count > 0) { + if (do_write) { + if (opt.no_action) { + i = count; + } else { + if (opt.save_image || opt.metadata_image) + i = fwrite(buf, 1, count, stream_out); +#ifdef HAVE_WINDOWS_H + else if (dev_out) + i = dev_out->d_ops->write(dev_out, + buf, count); +#endif + else + i = write(*(int *)fd, buf, count); + } + } else if (opt.restore_image) + i = read(*(int *)fd, buf, count); + else + i = dev->d_ops->read(dev, buf, count); + if (i < 0) { + if (errno != EAGAIN && errno != EINTR) + return -1; + } else if (i == 0 && !do_write && opt.restore_image) { + return -1; + } else { + count -= i; + buf = i + (char *) buf; + } + } + return 0; +} + + +static void rescue_sector(void *fd, u32 bytes_per_sector, off_t pos, void *buff) +{ + const char badsector_magic[] = "BadSectoR"; + struct ntfs_device *dev = fd; + + if (opt.restore_image) { + if (!opt.no_action + && (lseek(*(int *)fd, pos, SEEK_SET) == (off_t)-1)) + perr_exit("lseek"); + } else { + if (vol->dev->d_ops->seek(dev, pos, SEEK_SET) == (off_t)-1) + perr_exit("seek input"); + } + + if (read_all(fd, buff, bytes_per_sector) == -1) { + Printf("WARNING: Can't read sector at %llu, lost data.\n", + (unsigned long long)pos); + memset(buff, '?', bytes_per_sector); + memmove(buff, badsector_magic, sizeof(badsector_magic)); + } +} + +/* + * Read a cluster, try to rescue if cannot read + */ + +static void read_rescue(void *fd, char *buff, u32 csize, u32 bytes_per_sector, + u64 rescue_lcn) +{ + off_t rescue_pos; + + if (read_all(fd, buff, csize) == -1) { + + if (errno != EIO) + perr_exit("read_all"); + else if (opt.rescue){ + u32 i; + + rescue_pos = (off_t)(rescue_lcn * csize); + for (i = 0; i < csize; i += bytes_per_sector) + rescue_sector(fd, bytes_per_sector, + rescue_pos + i, buff + i); + } else { + Printf("%s", bad_sectors_warning_msg); + err_exit("Disk is faulty, can't make full backup!"); + } + } +} + +static void copy_cluster(int rescue, u64 rescue_lcn, u64 lcn) +{ + char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ + /* vol is NULL if opt.restore_image is set */ + s32 csize = le32_to_cpu(image_hdr.cluster_size); + BOOL backup_bootsector; + void *fd = (void *)&fd_in; + off_t rescue_pos; + NTFS_BOOT_SECTOR *bs; + le64 mask; + static u16 bytes_per_sector = NTFS_SECTOR_SIZE; + + if (!opt.restore_image) { + csize = vol->cluster_size; + bytes_per_sector = vol->sector_size; + fd = vol->dev; + } + + rescue_pos = (off_t)(rescue_lcn * csize); + + /* possible partial cluster holding the backup boot sector */ + backup_bootsector = (lcn + 1)*csize >= full_device_size; + if (backup_bootsector) { + csize = full_device_size - lcn*csize; + if (csize < 0) { + err_exit("Corrupted input, copy aborted"); + } + } + +// need reading when not about to write ? + if (read_all(fd, buff, csize) == -1) { + + if (errno != EIO) { + if (!errno && opt.restore_image) + err_exit("Short image file...\n"); + else + perr_exit("read_all"); + } + else if (rescue){ + s32 i; + for (i = 0; i < csize; i += bytes_per_sector) + rescue_sector(fd, bytes_per_sector, + rescue_pos + i, buff + i); + } else { + Printf("%s", bad_sectors_warning_msg); + err_exit("Disk is faulty, can't make full backup!"); + } + } + + /* Set the new serial number if requested */ + if (opt.new_serial + && !opt.save_image + && (!lcn || backup_bootsector)) { + /* + * For updating the backup boot sector, we need to + * know the sector size, but this is not recorded + * in the image header, so we collect it on the fly + * while reading the first boot sector. + */ + if (!lcn) { + bs = (NTFS_BOOT_SECTOR*)buff; + bytes_per_sector = le16_to_cpu(bs->bpb.bytes_per_sector); + if ((bytes_per_sector > csize) + || (bytes_per_sector < NTFS_SECTOR_SIZE)) + bytes_per_sector = NTFS_SECTOR_SIZE; + } else + bs = (NTFS_BOOT_SECTOR*)(buff + + csize - bytes_per_sector); + if (opt.new_serial & 2) + bs->volume_serial_number = volume_serial_number; + else { + mask = const_cpu_to_le64(~0x0ffffffffULL); + bs->volume_serial_number + = (volume_serial_number & mask) + | (bs->volume_serial_number & ~mask); + } + /* Show the new full serial after merging */ + if (!lcn) + Printf("New serial number : 0x%llx\n", + (long long)le64_to_cpu( + bs->volume_serial_number)); + } + + if (opt.save_image || (opt.metadata_image && wipe)) { + char cmd = CMD_NEXT; + if (write_all(&fd_out, &cmd, sizeof(cmd)) == -1) + perr_exit("write_all"); + } + + if ((!opt.metadata_image || wipe) + && (write_all(&fd_out, buff, csize) == -1)) { +#ifndef NO_STATFS + int err = errno; + perr_printf("Write failed"); + if (err == EIO && opt.stfs.f_type == 0x517b) + Printf("Apparently you tried to clone to a remote " + "Windows computer but they don't\nhave " + "efficient sparse file handling by default. " + "Please try a different method.\n"); + exit(1); +#else + perr_printf("Write failed"); +#endif + } +} + +static s64 lseek_out(int fd, s64 pos, int mode) +{ + s64 ret; + + if (dev_out) + ret = (dev_out->d_ops->seek)(dev_out, pos, mode); + else + ret = lseek(fd, pos, mode); + return (ret); +} + +static void lseek_to_cluster(s64 lcn) +{ + off_t pos; + + pos = (off_t)(lcn * vol->cluster_size); + + if (vol->dev->d_ops->seek(vol->dev, pos, SEEK_SET) == (off_t)-1) + perr_exit("lseek input"); + + if (opt.std_out || opt.save_image || opt.metadata_image) + return; + + if (lseek_out(fd_out, pos, SEEK_SET) == (off_t)-1) + perr_exit("lseek output"); +} + +static void gap_to_cluster(s64 gap) +{ + sle64 count; + char buf[1 + sizeof(count)]; + + if (gap) { + count = cpu_to_sle64(gap); + buf[0] = CMD_GAP; + memcpy(&buf[1], &count, sizeof(count)); + if (write_all(&fd_out, buf, sizeof(buf)) == -1) + perr_exit("write_all"); + } +} + +static void image_skip_clusters(s64 count) +{ + if (opt.save_image && count > 0) { + s64 count_buf; + char buff[1 + sizeof(count)]; + + buff[0] = CMD_GAP; + count_buf = cpu_to_sle64(count); + memcpy(buff + 1, &count_buf, sizeof(count_buf)); + + if (write_all(&fd_out, buff, sizeof(buff)) == -1) + perr_exit("write_all"); + } +} + +static void write_image_hdr(void) +{ + char alignment[IMAGE_HDR_ALIGN]; + + if (opt.save_image || opt.metadata_image) { + int alignsize = le32_to_cpu(image_hdr.offset_to_image_data) + - sizeof(image_hdr); + memset(alignment,0,IMAGE_HDR_ALIGN); + if ((alignsize < 0) + || write_all(&fd_out, &image_hdr, sizeof(image_hdr)) + || write_all(&fd_out, alignment, alignsize)) + perr_exit("write_all"); + } +} + +static void clone_ntfs(u64 nr_clusters, int more_use) +{ + u64 cl, last_cl; /* current and last used cluster */ + void *buf; + u32 csize = vol->cluster_size; + u64 p_counter = 0; + char alignment[IMAGE_HDR_ALIGN]; + struct progress_bar progress; + + if (opt.save_image) + Printf("Saving NTFS to image ...\n"); + else + Printf("Cloning NTFS ...\n"); + + if (opt.new_serial) + generate_serial_number(); + + buf = ntfs_calloc(csize); + if (!buf) + perr_exit("clone_ntfs"); + + progress_init(&progress, p_counter, nr_clusters, 100); + + if (opt.save_image) { + int alignsize = le32_to_cpu(image_hdr.offset_to_image_data) + - sizeof(image_hdr); + memset(alignment,0,IMAGE_HDR_ALIGN); + if ((alignsize < 0) + || write_all(&fd_out, &image_hdr, sizeof(image_hdr)) + || write_all(&fd_out, alignment, alignsize)) + perr_exit("write_all"); + } + + /* save suspicious clusters if required */ + if (more_use && opt.ignore_fs_check) { + compare_bitmaps(&lcn_bitmap, TRUE); + } + /* Examine up to the alternate boot sector */ + for (last_cl = cl = 0; cl <= (u64)vol->nr_clusters; cl++) { + + if (ntfs_bit_get(lcn_bitmap.bm, cl)) { + progress_update(&progress, ++p_counter); + lseek_to_cluster(cl); + image_skip_clusters(cl - last_cl - 1); + + copy_cluster(opt.rescue, cl, cl); + last_cl = cl; + continue; + } + + if (opt.std_out && !opt.save_image) { + progress_update(&progress, ++p_counter); + if (write_all(&fd_out, buf, csize) == -1) + perr_exit("write_all"); + } + } + image_skip_clusters(cl - last_cl - 1); + free(buf); +} + +static void write_empty_clusters(s32 csize, s64 count, + struct progress_bar *progress, u64 *p_counter) +{ + s64 i; + char buff[NTFS_MAX_CLUSTER_SIZE]; + + memset(buff, 0, csize); + + for (i = 0; i < count; i++) { + if (write_all(&fd_out, buff, csize) == -1) + perr_exit("write_all"); + progress_update(progress, ++(*p_counter)); + } +} + +static void restore_image(void) +{ + s64 pos = 0, count; + s32 csize = le32_to_cpu(image_hdr.cluster_size); + char cmd; + u64 p_counter = 0; + struct progress_bar progress; + + Printf("Restoring NTFS from image ...\n"); + + progress_init(&progress, p_counter, opt.std_out ? + sle64_to_cpu(image_hdr.nr_clusters) + 1 : + sle64_to_cpu(image_hdr.inuse) + 1, + 100); + + if (opt.new_serial) + generate_serial_number(); + + /* Restore up to the alternate boot sector */ + while (pos <= sle64_to_cpu(image_hdr.nr_clusters)) { + if (read_all(&fd_in, &cmd, sizeof(cmd)) == -1) { + if (pos == sle64_to_cpu(image_hdr.nr_clusters)) { + /* alternate boot sector no present in old images */ + Printf("Warning : no alternate boot" + " sector in image\n"); + break; + } else + perr_exit("read_all"); + } + + if (cmd == CMD_GAP) { + if (!image_is_host_endian) { + le64 lecount; + + /* little endian image, on any computer */ + if (read_all(&fd_in, &lecount, + sizeof(lecount)) == -1) + perr_exit("read_all"); + count = sle64_to_cpu(lecount); + } else { + /* big endian image on big endian computer */ + if (read_all(&fd_in, &count, + sizeof(count)) == -1) + perr_exit("read_all"); + } + if (!count) + err_exit("Bad offset at input location 0x%llx\n", + (long long)tellin(fd_in) - 9); + if (opt.std_out) { + if ((!p_counter && count) || (count < 0)) + err_exit("Cannot restore a metadata" + " image to stdout\n"); + else + write_empty_clusters(csize, count, + &progress, &p_counter); + } else { + if (((pos + count) < 0) + || ((pos + count) + > sle64_to_cpu(image_hdr.nr_clusters))) + err_exit("restore_image: corrupt image " + "at input offset %lld\n", + (long long)tellin(fd_in) - 9); + else { + if (!opt.no_action + && (lseek_out(fd_out, count * csize, + SEEK_CUR) == (off_t)-1)) + perr_exit("restore_image: lseek"); + } + } + pos += count; + } else if (cmd == CMD_NEXT) { + copy_cluster(0, 0, pos); + pos++; + progress_update(&progress, ++p_counter); + } else + err_exit("Invalid command code %d at input offset 0x%llx\n", + cmd, (long long)tellin(fd_in) - 1); + } +} + +static void wipe_index_entry_timestams(INDEX_ENTRY *e) +{ + static const struct timespec zero_time = { .tv_sec = 0, .tv_nsec = 0 }; + le64 timestamp = timespec2ntfs(zero_time); + + /* FIXME: can fall into infinite loop if corrupted */ + while (!(e->ie_flags & INDEX_ENTRY_END)) { + + e->key.file_name.creation_time = timestamp; + e->key.file_name.last_data_change_time = timestamp; + e->key.file_name.last_mft_change_time = timestamp; + e->key.file_name.last_access_time = timestamp; + + wiped_timestamp_data += 32; + + e = (INDEX_ENTRY *)((u8 *)e + le16_to_cpu(e->length)); + } +} + +static void wipe_index_allocation_timestamps(ntfs_inode *ni, ATTR_RECORD *attr) +{ + INDEX_ALLOCATION *indexa, *tmp_indexa; + INDEX_ENTRY *entry; + INDEX_ROOT *indexr; + u8 *bitmap, *byte; + int bit; + ntfs_attr *na; + ntfschar *name; + u32 name_len; + + indexr = ntfs_index_root_get(ni, attr); + if (!indexr) { + perr_printf("Failed to read $INDEX_ROOT attribute of inode " + "%lld", (long long)ni->mft_no); + return; + } + + if (indexr->type != AT_FILE_NAME) + goto out_indexr; + + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); + name_len = attr->name_length; + + byte = bitmap = ntfs_attr_readall(ni, AT_BITMAP, name, name_len, + NULL); + if (!byte) { + perr_printf("Failed to read $BITMAP attribute"); + goto out_indexr; + } + + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, name, name_len); + if (!na) { + perr_printf("Failed to open $INDEX_ALLOCATION attribute"); + goto out_bitmap; + } + + if (!na->data_size) + goto out_na; + + tmp_indexa = indexa = ntfs_malloc(na->data_size); + if (!tmp_indexa) + goto out_na; + + if (ntfs_attr_pread(na, 0, na->data_size, indexa) != na->data_size) { + perr_printf("Failed to read $INDEX_ALLOCATION attribute"); + goto out_indexa; + } + + bit = 0; + while ((u8 *)tmp_indexa < (u8 *)indexa + na->data_size) { + if (*byte & (1 << bit)) { + if (ntfs_mst_post_read_fixup((NTFS_RECORD *)tmp_indexa, + le32_to_cpu( + indexr->index_block_size))) { + perr_printf("Damaged INDX record"); + goto out_indexa; + } + entry = (INDEX_ENTRY *)((u8 *)tmp_indexa + le32_to_cpu( + tmp_indexa->index.entries_offset) + 0x18); + + wipe_index_entry_timestams(entry); + + if (ntfs_mft_usn_dec((MFT_RECORD *)tmp_indexa)) + perr_exit("ntfs_mft_usn_dec"); + + if (ntfs_mst_pre_write_fixup((NTFS_RECORD *)tmp_indexa, + le32_to_cpu( + indexr->index_block_size))) { + perr_printf("INDX write fixup failed"); + goto out_indexa; + } + } + tmp_indexa = (INDEX_ALLOCATION *)((u8 *)tmp_indexa + + le32_to_cpu(indexr->index_block_size)); + bit++; + if (bit > 7) { + bit = 0; + byte++; + } + } + if (ntfs_rl_pwrite(vol, na->rl, 0, 0, na->data_size, indexa) != na->data_size) + perr_printf("ntfs_rl_pwrite failed for inode %lld", + (long long)ni->mft_no); +out_indexa: + free(indexa); +out_na: + ntfs_attr_close(na); +out_bitmap: + free(bitmap); +out_indexr: + free(indexr); +} + +static void wipe_index_root_timestamps(ATTR_RECORD *attr, le64 timestamp) +{ + INDEX_ENTRY *entry; + INDEX_ROOT *iroot; + + iroot = (INDEX_ROOT *)((u8 *)attr + le16_to_cpu(attr->value_offset)); + entry = (INDEX_ENTRY *)((u8 *)iroot + + le32_to_cpu(iroot->index.entries_offset) + 0x10); + + while (!(entry->ie_flags & INDEX_ENTRY_END)) { + + if (iroot->type == AT_FILE_NAME) { + + entry->key.file_name.creation_time = timestamp; + entry->key.file_name.last_access_time = timestamp; + entry->key.file_name.last_data_change_time = timestamp; + entry->key.file_name.last_mft_change_time = timestamp; + + wiped_timestamp_data += 32; + + } else if (ntfs_names_are_equal(NTFS_INDEX_Q, + sizeof(NTFS_INDEX_Q) / 2 - 1, + (ntfschar *)((char *)attr + + le16_to_cpu(attr->name_offset)), + attr->name_length, CASE_SENSITIVE, NULL, 0)) { + + QUOTA_CONTROL_ENTRY *quota_q; + + quota_q = (QUOTA_CONTROL_ENTRY *)((u8 *)entry + + le16_to_cpu(entry->data_offset)); + /* + * FIXME: no guarantee it's indeed /$Extend/$Quota:$Q. + * For now, as a minimal safeguard, we check only for + * quota version 2 ... + */ + if (le32_to_cpu(quota_q->version) == 2) { + quota_q->change_time = timestamp; + wiped_timestamp_data += 4; + } + } + + entry = (INDEX_ENTRY*)((u8*)entry + le16_to_cpu(entry->length)); + } +} + +#define WIPE_TIMESTAMPS(atype, attr, timestamp) \ +do { \ + atype *ats; \ + ats = (atype *)((char *)(attr) + le16_to_cpu((attr)->value_offset)); \ + \ + ats->creation_time = (timestamp); \ + ats->last_data_change_time = (timestamp); \ + ats->last_mft_change_time= (timestamp); \ + ats->last_access_time = (timestamp); \ + \ + wiped_timestamp_data += 32; \ + \ +} while (0) + +static void wipe_timestamps(ntfs_walk_clusters_ctx *image) +{ + static const struct timespec zero_time = { .tv_sec = 0, .tv_nsec = 0 }; + ATTR_RECORD *a = image->ctx->attr; + le64 timestamp = timespec2ntfs(zero_time); + + if (a->type == AT_FILE_NAME) + WIPE_TIMESTAMPS(FILE_NAME_ATTR, a, timestamp); + + else if (a->type == AT_STANDARD_INFORMATION) + WIPE_TIMESTAMPS(STANDARD_INFORMATION, a, timestamp); + + else if (a->type == AT_INDEX_ROOT) + wipe_index_root_timestamps(a, timestamp); +} + +static void wipe_resident_data(ntfs_walk_clusters_ctx *image) +{ + ATTR_RECORD *a; + u32 i; + int n = 0; + u8 *p; + + a = image->ctx->attr; + p = (u8*)a + le16_to_cpu(a->value_offset); + + if (image->ni->mft_no <= LAST_METADATA_INODE) + return; + + if (a->type != AT_DATA) + return; + + for (i = 0; i < le32_to_cpu(a->value_length); i++) { + if (p[i]) { + p[i] = 0; + n++; + } + } + + wiped_resident_data += n; +} + +static int wipe_data(char *p, int pos, int len) +{ + int wiped = 0; + + for (p += pos; --len >= 0;) { + if (p[len]) { + p[len] = 0; + wiped++; + } + } + + return wiped; +} + +static void wipe_unused_mft_data(ntfs_inode *ni) +{ + int unused; + MFT_RECORD *m = ni->mrec; + + /* FIXME: broken MFTMirr update was fixed in libntfs, check if OK now */ + if (ni->mft_no <= LAST_METADATA_INODE) + return; + + unused = le32_to_cpu(m->bytes_allocated) - le32_to_cpu(m->bytes_in_use); + wiped_unused_mft_data += wipe_data((char *)m, + le32_to_cpu(m->bytes_in_use), unused); +} + +static void wipe_unused_mft(ntfs_inode *ni) +{ + int unused; + MFT_RECORD *m = ni->mrec; + + /* FIXME: broken MFTMirr update was fixed in libntfs, check if OK now */ + if (ni->mft_no <= LAST_METADATA_INODE) + return; + + unused = le32_to_cpu(m->bytes_in_use) - sizeof(MFT_RECORD); + wiped_unused_mft += wipe_data((char *)m, sizeof(MFT_RECORD), unused); +} + +static void clone_logfile_parts(ntfs_walk_clusters_ctx *image, runlist *rl) +{ + s64 offset = 0, lcn, vcn; + + while (1) { + + vcn = offset / image->ni->vol->cluster_size; + lcn = ntfs_rl_vcn_to_lcn(rl, vcn); + if (lcn < 0) + break; + + lseek_to_cluster(lcn); + + if ((lcn + 1) != image->current_lcn) { + /* do not duplicate a cluster */ + if (opt.metadata_image && wipe) + gap_to_cluster(lcn - image->current_lcn); + + copy_cluster(opt.rescue, lcn, lcn); + } + image->current_lcn = lcn + 1; + if (opt.metadata_image && !wipe) + image->inuse++; + + if (offset == 0) + offset = NTFS_BLOCK_SIZE >> 1; + else + offset <<= 1; + } +} + +/* + * In-memory wiping of MFT record or MFTMirr record + * (only for metadata images) + * + * The resident data and (optionally) the timestamps are wiped. + */ + +static void wipe_mft(char *mrec, u32 mrecsz, u64 mft_no) +{ + ntfs_walk_clusters_ctx image; + ntfs_attr_search_ctx *ctx; + ntfs_inode ni; + + ni.mft_no = mft_no; + ni.mrec = (MFT_RECORD*)mrec; + ni.vol = vol; /* Hmm */ + image.ni = ∋ + ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)mrec,mrecsz,FALSE); + wipe_unused_mft_data(&ni); + if (!(((MFT_RECORD*)mrec)->flags & MFT_RECORD_IN_USE)) { + wipe_unused_mft(&ni); + } else { + /* ctx with no ntfs_inode prevents from searching external attrs */ + if (!(ctx = ntfs_attr_get_search_ctx((ntfs_inode*)NULL, (MFT_RECORD*)mrec))) + perr_exit("ntfs_get_attr_search_ctx"); + + while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0, + NULL, 0, ctx)) { + if (ctx->attr->type == AT_END) + break; + + image.ctx = ctx; + if (!ctx->attr->non_resident + && (mft_no > LAST_METADATA_INODE)) + wipe_resident_data(&image); + if (!opt.preserve_timestamps) + wipe_timestamps(&image); + } + ntfs_attr_put_search_ctx(ctx); + } + ntfs_mft_usn_dec((MFT_RECORD*)mrec); + ntfs_mst_pre_write_fixup((NTFS_RECORD*)mrec,mrecsz); +} + +/* + * In-memory wiping of a directory record (I30) + * (only for metadata images) + * + * The timestamps are (optionally) wiped + */ + +static void wipe_indx(char *mrec, u32 mrecsz) +{ + INDEX_ENTRY *entry; + INDEX_ALLOCATION *indexa; + + if (ntfs_mst_post_read_fixup((NTFS_RECORD *)mrec, mrecsz)) { + perr_printf("Damaged INDX record"); + goto out_indexa; + } + indexa = (INDEX_ALLOCATION*)mrec; + /* + * The index bitmap is not checked, obsoleted records are + * wiped if they pass the safety checks + */ + if ((indexa->magic == magic_INDX) + && (le32_to_cpu(indexa->index.entries_offset) >= sizeof(INDEX_HEADER)) + && (le32_to_cpu(indexa->index.allocated_size) <= mrecsz)) { + entry = (INDEX_ENTRY *)((u8 *)mrec + le32_to_cpu( + indexa->index.entries_offset) + 0x18); + wipe_index_entry_timestams(entry); + } + + if (ntfs_mft_usn_dec((MFT_RECORD *)mrec)) + perr_exit("ntfs_mft_usn_dec"); + + if (ntfs_mst_pre_write_fixup((NTFS_RECORD *)mrec, mrecsz)) { + perr_printf("INDX write fixup failed"); + goto out_indexa; + } +out_indexa : ; +} + +/* + * Output a set of related clusters (MFT record or index block) + */ + +static void write_set(char *buff, u32 csize, s64 *current_lcn, + runlist_element *rl, u32 wi, u32 wj, u32 cnt) +{ + u32 k; + s64 target_lcn; + char cmd = CMD_NEXT; + + for (k=0; k<cnt; k++) { + target_lcn = rl[wi].lcn + wj; + if (target_lcn != *current_lcn) + gap_to_cluster(target_lcn - *current_lcn); + if ((write_all(&fd_out, &cmd, sizeof(cmd)) == -1) + || (write_all(&fd_out, &buff[k*csize], csize) == -1)) + perr_exit("Failed to write_all"); + *current_lcn = target_lcn + 1; + + if (++wj >= rl[wi].length) { + wj = 0; + wi++; + } + } +} + +/* + * Copy and wipe the full MFT or MFTMirr data. + * (only for metadata images) + * + * Data are read and written by full clusters, but the wiping is done + * per MFT record. + */ + +static void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl) +{ + char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ + void *fd; + s64 mft_no; + u32 mft_record_size; + u32 csize; + u32 bytes_per_sector; + u32 records_per_set; + u32 clusters_per_set; + u32 wi,wj; /* indexes for reading */ + u32 ri,rj; /* indexes for writing */ + u32 k; /* lcn within run */ + u32 r; /* mft_record within set */ + s64 current_lcn; + + current_lcn = image->current_lcn; + mft_record_size = image->ni->vol->mft_record_size; + csize = image->ni->vol->cluster_size; + bytes_per_sector = image->ni->vol->sector_size; + fd = image->ni->vol->dev; + /* + * Depending on the sizes, there may be several records + * per cluster, or several clusters per record. + */ + if (csize >= mft_record_size) { + records_per_set = csize/mft_record_size; + clusters_per_set = 1; + } else { + clusters_per_set = mft_record_size/csize; + records_per_set = 1; + } + mft_no = 0; + ri = rj = 0; + wi = wj = 0; + if (rl[ri].length) + lseek_to_cluster(rl[ri].lcn); + while (rl[ri].length) { + for (k=0; (k<clusters_per_set) && rl[ri].length; k++) { + read_rescue(fd, &buff[k*csize], csize, bytes_per_sector, + rl[ri].lcn + rj); + if (++rj >= rl[ri].length) { + rj = 0; + if (rl[++ri].length) + lseek_to_cluster(rl[ri].lcn); + } + } + if (k == clusters_per_set) { + for (r=0; r<records_per_set; r++) { + if (!strncmp(&buff[r*mft_record_size],"FILE",4)) + wipe_mft(&buff[r*mft_record_size], + mft_record_size, mft_no); + mft_no++; + } + write_set(buff, csize, ¤t_lcn, + rl, wi, wj, clusters_per_set); + wj += clusters_per_set; + while (rl[wi].length && (wj >= rl[wi].length)) + wj -= rl[wi++].length; + } else { + err_exit("Short last MFT record\n"); + } + } + image->current_lcn = current_lcn; +} + +/* + * Copy and wipe the non-resident part of a directory index + * (only for metadata images) + * + * Data are read and written by full clusters, but the wiping is done + * per index record. + */ + +static void copy_wipe_i30(ntfs_walk_clusters_ctx *image, runlist *rl) +{ + char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ + void *fd; + u32 indx_record_size; + u32 csize; + u32 bytes_per_sector; + u32 records_per_set; + u32 clusters_per_set; + u32 wi,wj; /* indexes for reading */ + u32 ri,rj; /* indexes for writing */ + u32 k; /* lcn within run */ + u32 r; /* mft_record within set */ + s64 current_lcn; + + current_lcn = image->current_lcn; + csize = image->ni->vol->cluster_size; + bytes_per_sector = image->ni->vol->sector_size; + fd = image->ni->vol->dev; + /* + * Depending on the sizes, there may be several records + * per cluster, or several clusters per record. + */ + indx_record_size = image->ni->vol->indx_record_size; + if (csize >= indx_record_size) { + records_per_set = csize/indx_record_size; + clusters_per_set = 1; + } else { + clusters_per_set = indx_record_size/csize; + records_per_set = 1; + } + ri = rj = 0; + wi = wj = 0; + if (rl[ri].length) + lseek_to_cluster(rl[ri].lcn); + while (rl[ri].length) { + for (k=0; (k<clusters_per_set) && rl[ri].length; k++) { + read_rescue(fd, &buff[k*csize], csize, bytes_per_sector, + rl[ri].lcn + rj); + if (++rj >= rl[ri].length) { + rj = 0; + if (rl[++ri].length) + lseek_to_cluster(rl[ri].lcn); + } + } + if (k == clusters_per_set) { + /* wipe records_per_set records */ + if (!opt.preserve_timestamps) + for (r=0; r<records_per_set; r++) { + if (!strncmp(&buff[r*indx_record_size],"INDX",4)) + wipe_indx(&buff[r*indx_record_size], + indx_record_size); + } + write_set(buff, csize, ¤t_lcn, + rl, wi, wj, clusters_per_set); + wj += clusters_per_set; + while (rl[wi].length && (wj >= rl[wi].length)) + wj -= rl[wi++].length; + } else { + err_exit("Short last directory index record\n"); + } + } + image->current_lcn = current_lcn; +} + +static void dump_clusters(ntfs_walk_clusters_ctx *image, runlist *rl) +{ + s64 i, len; /* number of clusters to copy */ + + if (opt.restore_image) + err_exit("Bug : invalid dump_clusters()\n"); + + if ((opt.std_out && !opt.metadata_image) || !opt.metadata) + return; + if (!(len = is_critical_metadata(image, rl))) + return; + + lseek_to_cluster(rl->lcn); + if (opt.metadata_image ? wipe : !wipe) { + if (opt.metadata_image) + gap_to_cluster(rl->lcn - image->current_lcn); + /* FIXME: this could give pretty suboptimal performance */ + for (i = 0; i < len; i++) + copy_cluster(opt.rescue, rl->lcn + i, rl->lcn + i); + if (opt.metadata_image) + image->current_lcn = rl->lcn + len; + } +} + +static void walk_runs(struct ntfs_walk_cluster *walk) +{ + int i, j; + runlist *rl; + ATTR_RECORD *a; + ntfs_attr_search_ctx *ctx; + BOOL mft_data; + BOOL index_i30; + + ctx = walk->image->ctx; + a = ctx->attr; + + if (!a->non_resident) { + if (wipe) { + wipe_resident_data(walk->image); + if (!opt.preserve_timestamps) + wipe_timestamps(walk->image); + } + return; + } + + if (wipe + && !opt.preserve_timestamps + && walk->image->ctx->attr->type == AT_INDEX_ALLOCATION) + wipe_index_allocation_timestamps(walk->image->ni, a); + + if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) + perr_exit("ntfs_decompress_mapping_pairs"); + + /* special wipings for MFT records and directory indexes */ + mft_data = ((walk->image->ni->mft_no == FILE_MFT) + || (walk->image->ni->mft_no == FILE_MFTMirr)) + && (a->type == AT_DATA); + index_i30 = (walk->image->ctx->attr->type == AT_INDEX_ALLOCATION) + && (a->name_length == 4) + && !memcmp((char*)a + le16_to_cpu(a->name_offset), + NTFS_INDEX_I30,8); + + for (i = 0; rl[i].length; i++) { + s64 lcn = rl[i].lcn; + s64 lcn_length = rl[i].length; + + if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) + continue; + + /* FIXME: ntfs_mapping_pairs_decompress should return error */ + if (lcn < 0 || lcn_length < 0) + err_exit("Corrupt runlist in inode %lld attr %x LCN " + "%llx length %llx\n", + (long long)ctx->ntfs_ino->mft_no, + (unsigned int)le32_to_cpu(a->type), + (long long)lcn, (long long)lcn_length); + + if (opt.metadata_image ? wipe && !mft_data && !index_i30 : !wipe) + dump_clusters(walk->image, rl + i); + + for (j = 0; j < lcn_length; j++) { + u64 k = (u64)lcn + j; + if (ntfs_bit_get_and_set(lcn_bitmap.bm, k, 1)) + err_exit("Cluster %llu referenced twice!\n" + "You didn't shutdown your Windows " + "properly?\n", (unsigned long long)k); + } + + if (!opt.metadata_image) + walk->image->inuse += lcn_length; + /* + * For a metadata image, we have to compute the + * number of metadata clusters for the percentages + * to be displayed correctly while restoring. + */ + if (!wipe && opt.metadata_image) { + if ((walk->image->ni->mft_no == FILE_LogFile) + && (walk->image->ctx->attr->type == AT_DATA)) { + /* 16 KiB of FILE_LogFile */ + walk->image->inuse + += is_critical_metadata(walk->image,rl); + } else { + if ((walk->image->ni->mft_no + <= LAST_METADATA_INODE) + || (walk->image->ctx->attr->type != AT_DATA)) + walk->image->inuse += lcn_length; + } + } + } + if (wipe && opt.metadata_image) { + ntfs_attr *na; + /* + * Non-resident metadata has to be wiped globally, + * because its logical blocks may be larger than + * a cluster and split over two extents. + */ + if (mft_data && !a->lowest_vcn) { + na = ntfs_attr_open(walk->image->ni, + AT_DATA, NULL, 0); + if (na) { + na->rl = rl; + rl = (runlist_element*)NULL; + if (!ntfs_attr_map_whole_runlist(na)) { + copy_wipe_mft(walk->image,na->rl); + } else + perr_exit("Failed to map data of inode %lld", + (long long)walk->image->ni->mft_no); + ntfs_attr_close(na); + } else + perr_exit("Failed to open data of inode %lld", + (long long)walk->image->ni->mft_no); + } + if (index_i30 && !a->lowest_vcn) { + na = ntfs_attr_open(walk->image->ni, + AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (na) { + na->rl = rl; + rl = (runlist_element*)NULL; + if (!ntfs_attr_map_whole_runlist(na)) { + copy_wipe_i30(walk->image,na->rl); + } else + perr_exit("Failed to map index of inode %lld", + (long long)walk->image->ni->mft_no); + ntfs_attr_close(na); + } else + perr_exit("Failed to open index of inode %lld", + (long long)walk->image->ni->mft_no); + } + } + if (opt.metadata + && (opt.metadata_image || !wipe) + && (walk->image->ni->mft_no == FILE_LogFile) + && (walk->image->ctx->attr->type == AT_DATA)) + clone_logfile_parts(walk->image, rl); + + free(rl); +} + + +static void walk_attributes(struct ntfs_walk_cluster *walk) +{ + ntfs_attr_search_ctx *ctx; + + if (!(ctx = ntfs_attr_get_search_ctx(walk->image->ni, NULL))) + perr_exit("ntfs_get_attr_search_ctx"); + + while (!ntfs_attrs_walk(ctx)) { + if (ctx->attr->type == AT_END) + break; + + walk->image->ctx = ctx; + walk_runs(walk); + } + + ntfs_attr_put_search_ctx(ctx); +} + +/* + * Compare the actual bitmap to the list of clusters + * allocated to identified files. + * + * Clusters found in use, though not marked in the bitmap are copied + * if the option --ignore-fs-checks is set. + */ + +static int compare_bitmaps(struct bitmap *a, BOOL copy) +{ + s64 i, pos, count; + int mismatch = 0; + int more_use = 0; + s64 new_cl; + u8 bm[NTFS_BUF_SIZE]; + + Printf("Accounting clusters ...\n"); + + pos = 0; + new_cl = 0; + while (1) { + count = ntfs_attr_pread(vol->lcnbmp_na, pos, NTFS_BUF_SIZE, bm); + if (count == -1) + perr_exit("Couldn't get $Bitmap $DATA"); + + if (count == 0) { + /* the backup bootsector need not be accounted for */ + if (((vol->nr_clusters + 7) >> 3) > pos) + err_exit("$Bitmap size is smaller than expected" + " (%lld < %lld)\n", + (long long)pos, (long long)a->size); + break; + } + + for (i = 0; i < count; i++, pos++) { + s64 cl; /* current cluster */ + + if (a->size <= pos) + goto done; + + if (a->bm[pos] == bm[i]) + continue; + + for (cl = pos * 8; cl < (pos + 1) * 8; cl++) { + char bit; + + bit = ntfs_bit_get(a->bm, cl); + if (bit == ntfs_bit_get(bm, i * 8 + cl % 8)) + continue; + + if (!bit) + more_use++; + if (opt.ignore_fs_check && !bit && copy) { + lseek_to_cluster(cl); + if (opt.save_image + || (opt.metadata + && opt.metadata_image)) { + gap_to_cluster(cl - new_cl); + new_cl = cl + 1; + } + copy_cluster(opt.rescue, cl, cl); + } + + if (++mismatch > 10) + continue; + + Printf("Cluster accounting failed at %lld " + "(0x%llx): %s cluster in $Bitmap\n", + (long long)cl, (unsigned long long)cl, + bit ? "missing" : "extra"); + } + } + } +done: + if (mismatch) { + Printf("Totally %d cluster accounting mismatches.\n", mismatch); + if (opt.ignore_fs_check) { + Printf("WARNING: The NTFS inconsistency was overruled " + "by the --ignore-fs-check option.\n"); + if (new_cl) { + gap_to_cluster(-new_cl); + } + return (more_use); + } + err_exit("Filesystem check failed! Windows wasn't shutdown " + "properly or inconsistent\nfilesystem. Please run " + "chkdsk /f on Windows then reboot it TWICE.\n"); + } + return (more_use); +} + + +static void mft_record_write_with_same_usn(ntfs_volume *volume, ntfs_inode *ni) +{ + if (ntfs_mft_usn_dec(ni->mrec)) + perr_exit("ntfs_mft_usn_dec"); + + if (ntfs_mft_record_write(volume, ni->mft_no, ni->mrec)) + perr_exit("ntfs_mft_record_write"); +} + +static void mft_inode_write_with_same_usn(ntfs_volume *volume, ntfs_inode *ni) +{ + s32 i; + + mft_record_write_with_same_usn(volume, ni); + + if (ni->nr_extents <= 0) + return; + + for (i = 0; i < ni->nr_extents; ++i) { + ntfs_inode *eni = ni->extent_nis[i]; + mft_record_write_with_same_usn(volume, eni); + } +} + +static int walk_clusters(ntfs_volume *volume, struct ntfs_walk_cluster *walk) +{ + s64 inode = 0; + s64 last_mft_rec; + u64 nr_clusters; + ntfs_inode *ni; + struct progress_bar progress; + + if (opt.restore_image || (!opt.metadata && wipe)) + err_exit("Bug : invalid walk_clusters()\n"); + Printf("Scanning volume ...\n"); + + last_mft_rec = (volume->mft_na->initialized_size >> + volume->mft_record_size_bits) - 1; + walk->image->current_lcn = 0; + progress_init(&progress, inode, last_mft_rec, 100); + + NVolSetNoFixupWarn(volume); + for (; inode <= last_mft_rec; inode++) { + + int err, deleted_inode; + MFT_REF mref = (MFT_REF)inode; + + progress_update(&progress, inode); + + /* FIXME: Terrible kludge for libntfs not being able to return + a deleted MFT record as inode */ + ni = ntfs_calloc(sizeof(ntfs_inode)); + if (!ni) + perr_exit("walk_clusters"); + + ni->vol = volume; + + err = ntfs_file_record_read(volume, mref, &ni->mrec, NULL); + if (err == -1) { + free(ni); + continue; + } + + deleted_inode = !(ni->mrec->flags & MFT_RECORD_IN_USE); + + if (deleted_inode && !opt.metadata_image) { + + ni->mft_no = MREF(mref); + if (wipe) { + wipe_unused_mft(ni); + wipe_unused_mft_data(ni); + mft_record_write_with_same_usn(volume, ni); + } + } + + free(ni->mrec); + free(ni); + + if (deleted_inode) + continue; + + if ((ni = ntfs_inode_open(volume, mref)) == NULL) { + /* FIXME: continue only if it make sense, e.g. + MFT record not in use based on $MFT bitmap */ + if (errno == EIO || errno == ENOENT) + continue; + perr_exit("Reading inode %lld failed", + (long long)inode); + } + + if (wipe) + nr_used_mft_records++; + + if (ni->mrec->base_mft_record) + goto out; + + walk->image->ni = ni; + walk_attributes(walk); +out: + if (wipe && !opt.metadata_image) { + int i; + + wipe_unused_mft_data(ni); + for (i = 0; i < ni->nr_extents; ++i) { + wipe_unused_mft_data(ni->extent_nis[i]); + } + mft_inode_write_with_same_usn(volume, ni); + } + + if (ntfs_inode_close(ni)) + perr_exit("ntfs_inode_close for inode %lld", + (long long)inode); + } + if (opt.metadata) { + if (opt.metadata_image && wipe && opt.ignore_fs_check) { + gap_to_cluster(-walk->image->current_lcn); + compare_bitmaps(&lcn_bitmap, TRUE); + walk->image->current_lcn = 0; + } + if (opt.metadata_image ? wipe : !wipe) { + /* also get the backup bootsector */ + nr_clusters = vol->nr_clusters; + lseek_to_cluster(nr_clusters); + if (opt.metadata_image && wipe) + gap_to_cluster(nr_clusters + - walk->image->current_lcn); + copy_cluster(opt.rescue, nr_clusters, nr_clusters); + walk->image->current_lcn = nr_clusters; + } + /* Not counted, for compatibility with older versions */ + if (!opt.metadata_image) + walk->image->inuse++; + } + return 0; +} + + +/* + * $Bitmap can overlap the end of the volume. Any bits in this region + * must be set. This region also encompasses the backup boot sector. + */ +static void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm) +{ + for (; cluster < bm->size << 3; cluster++) + ntfs_bit_set(bm->bm, (u64)cluster, 1); +} + + +/* + * Allocate a block of memory with one bit for each cluster of the disk. + * All the bits are set to 0, except those representing the region beyond the + * end of the disk. + */ +static void setup_lcn_bitmap(void) +{ + /* Determine lcn bitmap byte size and allocate it. */ + /* include the alternate boot sector in the bitmap count */ + lcn_bitmap.size = rounded_up_division(vol->nr_clusters + 1, 8); + + lcn_bitmap.bm = ntfs_calloc(lcn_bitmap.size); + if (!lcn_bitmap.bm) + perr_exit("Failed to allocate internal buffer"); + + bitmap_file_data_fixup(vol->nr_clusters, &lcn_bitmap); +} + + +static s64 volume_size(ntfs_volume *volume, s64 nr_clusters) +{ + return nr_clusters * volume->cluster_size; +} + + +static void print_volume_size(const char *str, s64 bytes) +{ + Printf("%s: %lld bytes (%lld MB)\n", str, (long long)bytes, + (long long)rounded_up_division(bytes, NTFS_MBYTE)); +} + + +static void print_disk_usage(const char *spacer, u32 cluster_size, + s64 nr_clusters, s64 inuse) +{ + s64 total, used; + + total = nr_clusters * cluster_size; + used = inuse * cluster_size; + + Printf("Space in use %s: %lld MB (%.1f%%) ", spacer, + (long long)rounded_up_division(used, NTFS_MBYTE), + 100.0 * ((float)used / total)); + + Printf("\n"); +} + +static void print_image_info(void) +{ + Printf("Ntfsclone image version: %d.%d\n", + image_hdr.major_ver, image_hdr.minor_ver); + Printf("Cluster size : %u bytes\n", + (unsigned)le32_to_cpu(image_hdr.cluster_size)); + print_volume_size("Image volume size ", + sle64_to_cpu(image_hdr.nr_clusters) * + le32_to_cpu(image_hdr.cluster_size)); + Printf("Image device size : %lld bytes\n", + (long long)sle64_to_cpu(image_hdr.device_size)); + print_disk_usage(" ", le32_to_cpu(image_hdr.cluster_size), + sle64_to_cpu(image_hdr.nr_clusters), + sle64_to_cpu(image_hdr.inuse)); + Printf("Offset to image data : %u (0x%x) bytes\n", + (unsigned)le32_to_cpu(image_hdr.offset_to_image_data), + (unsigned)le32_to_cpu(image_hdr.offset_to_image_data)); +} + +static void check_if_mounted(const char *device, unsigned long new_mntflag) +{ + unsigned long mntflag; + + if (ntfs_check_if_mounted(device, &mntflag)) + perr_exit("Failed to check '%s' mount state", device); + + if (mntflag & NTFS_MF_MOUNTED) { + if (!(mntflag & NTFS_MF_READONLY)) + err_exit("Device '%s' is mounted read-write. " + "You must 'umount' it first.\n", device); + if (!new_mntflag) + err_exit("Device '%s' is mounted. " + "You must 'umount' it first.\n", device); + } +} + +/** + * mount_volume - + * + * First perform some checks to determine if the volume is already mounted, or + * is dirty (Windows wasn't shutdown properly). If everything is OK, then mount + * the volume (load the metadata into memory). + */ +static void mount_volume(unsigned long new_mntflag) +{ + check_if_mounted(opt.volume, new_mntflag); + + if (!(vol = ntfs_mount(opt.volume, new_mntflag))) { + + int err = errno; + + perr_printf("Opening '%s' as NTFS failed", opt.volume); + if (err == EINVAL) { + Printf("Apparently device '%s' doesn't have a " + "valid NTFS. Maybe you selected\nthe whole " + "disk instead of a partition (e.g. /dev/hda, " + "not /dev/hda1)?\n", opt.volume); + } + /* + * Retry with recovering the log file enabled. + * Normally avoided in order to get the original log file + * data, but needed when remounting the metadata of a + * volume improperly unmounted from Windows. + */ + if (!(new_mntflag & (NTFS_MNT_RDONLY | NTFS_MNT_RECOVER))) { + Printf("Trying to recover...\n"); + vol = ntfs_mount(opt.volume, + new_mntflag | NTFS_MNT_RECOVER); + Printf("... %s\n",(vol ? "Successful" : "Failed")); + } + if (!vol) + exit(1); + } + + if (vol->flags & VOLUME_IS_DIRTY) + if (opt.force-- <= 0) + err_exit(dirty_volume_msg, opt.volume); + + if (NTFS_MAX_CLUSTER_SIZE < vol->cluster_size) + err_exit("Cluster size %u is too large!\n", + (unsigned int)vol->cluster_size); + + Printf("NTFS volume version: %d.%d\n", vol->major_ver, vol->minor_ver); + if (ntfs_version_is_supported(vol)) + perr_exit("Unknown NTFS version"); + + Printf("Cluster size : %u bytes\n", + (unsigned int)vol->cluster_size); + print_volume_size("Current volume size", + volume_size(vol, vol->nr_clusters)); +} + +static struct ntfs_walk_cluster backup_clusters = { NULL, NULL }; + +static int device_offset_valid(int fd, s64 ofs) +{ + char ch; + + if (lseek(fd, ofs, SEEK_SET) >= 0 && read(fd, &ch, 1) == 1) + return 0; + return -1; +} + +static s64 device_size_get(int fd) +{ + s64 high, low; +#ifdef BLKGETSIZE64 + { u64 size; + + if (ioctl(fd, BLKGETSIZE64, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu " + "(0x%llx).\n", (unsigned long long)size, + (unsigned long long)size); + return (s64)size; + } + } +#endif +#ifdef BLKGETSIZE + { unsigned long size; + + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu " + "(0x%lx).\n", size, size); + return (s64)size * 512; + } + } +#endif +#ifdef FDGETPRM + { struct floppy_struct this_floppy; + + if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { + ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu " + "(0x%lx).\n", this_floppy.size, + this_floppy.size); + return (s64)this_floppy.size * 512; + } + } +#endif + /* + * We couldn't figure it out by using a specialized ioctl, + * so do binary search to find the size of the device. + */ + low = 0LL; + for (high = 1024LL; !device_offset_valid(fd, high); high <<= 1) + low = high; + while (low < high - 1LL) { + const s64 mid = (low + high) / 2; + + if (!device_offset_valid(fd, mid)) + low = mid; + else + high = mid; + } + lseek(fd, 0LL, SEEK_SET); + return (low + 1LL); +} + +static void fsync_clone(int fd) +{ + Printf("Syncing ...\n"); + if (opt.save_image && stream_out && fflush(stream_out)) + perr_exit("fflush"); + if (fsync(fd) && errno != EINVAL) + perr_exit("fsync"); +} + +static void set_filesize(s64 filesize) +{ +#ifndef NO_STATFS + long fs_type = 0; /* Unknown filesystem type */ + + if (fstatfs(fd_out, &opt.stfs) == -1) + Printf("WARNING: Couldn't get filesystem type: " + "%s\n", strerror(errno)); + else + fs_type = opt.stfs.f_type; + + if (fs_type == 0x52654973) + Printf("WARNING: You're using ReiserFS, it has very poor " + "performance creating\nlarge sparse files. The next " + "operation might take a very long time!\n" + "Creating sparse output file ...\n"); + else if (fs_type == 0x517b) + Printf("WARNING: You're using SMBFS and if the remote share " + "isn't Samba but a Windows\ncomputer then the clone " + "operation will be very inefficient and may fail!\n"); +#endif + + if (!opt.no_action && (ftruncate(fd_out, filesize) == -1)) { + int err = errno; + perr_printf("ftruncate failed for file '%s'", opt.output); +#ifndef NO_STATFS + if (fs_type) + Printf("Destination filesystem type is 0x%lx.\n", + (unsigned long)fs_type); +#endif + if (err == E2BIG) { + Printf("Your system or the destination filesystem " + "doesn't support large files.\n"); +#ifndef NO_STATFS + if (fs_type == 0x517b) { + Printf("SMBFS needs minimum Linux kernel " + "version 2.4.25 and\n the 'lfs' option" + "\nfor smbmount to have large " + "file support.\n"); + } +#endif + } else if (err == EPERM) { + Printf("Apparently the destination filesystem doesn't " + "support sparse files.\nYou can overcome this " + "by using the more efficient --save-image " + "option\nof ntfsclone. Use the --restore-image " + "option to restore the image.\n"); + } + exit(1); + } + /* + * If truncate just created a sparse file, the ability + * to generically store big files has been checked, but no + * space has been reserved and available space has probably + * not been checked. Better reset the file so that we write + * sequentially to the end. + */ + if (!opt.no_action) { +#ifdef HAVE_WINDOWS_H + if (ftruncate(fd_out, 0)) + Printf("Failed to reset the output file.\n"); +#else + struct stat st; + int s; + + s = fstat(fd_out, &st); + if (s || (!st.st_blocks && ftruncate(fd_out, 0))) + Printf("Failed to reset the output file.\n"); +#endif + /* Proceed even if ftruncate failed */ + } +} + +static s64 open_image(void) +{ + if (strcmp(opt.volume, "-") == 0) { + if ((fd_in = fileno(stdin)) == -1) + perr_exit("fileno for stdin failed"); +#ifdef HAVE_WINDOWS_H + if (setmode(fd_in,O_BINARY) == -1) + perr_exit("setting binary stdin failed"); +#endif + } else { + if ((fd_in = open(opt.volume, O_RDONLY | O_BINARY)) == -1) + perr_exit("failed to open image"); + } + if (read_all(&fd_in, &image_hdr, NTFSCLONE_IMG_HEADER_SIZE_OLD) == -1) + perr_exit("read_all"); + if (memcmp(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE) != 0) + err_exit("Input file is not an image! (invalid magic)\n"); + if (image_hdr.major_ver < NTFSCLONE_IMG_VER_MAJOR_ENDIANNESS_SAFE) { + image_hdr.major_ver = NTFSCLONE_IMG_VER_MAJOR; + image_hdr.minor_ver = NTFSCLONE_IMG_VER_MINOR; +#if (__BYTE_ORDER == __BIG_ENDIAN) + /* + * old image read on a big endian computer, + * assuming it was created big endian and read cpu-wise, + * so we should translate to little endian + */ + Printf("Old image format detected. If the image was created " + "on a little endian architecture it will not " + "work. Use a more recent version of " + "ntfsclone to recreate the image.\n"); + image_hdr.cluster_size = cpu_to_le32(image_hdr.cluster_size); + image_hdr.device_size = cpu_to_sle64(image_hdr.device_size); + image_hdr.nr_clusters = cpu_to_sle64(image_hdr.nr_clusters); + image_hdr.inuse = cpu_to_sle64(image_hdr.inuse); +#endif + image_hdr.offset_to_image_data = + const_cpu_to_le32((sizeof(image_hdr) + + IMAGE_HDR_ALIGN - 1) & -IMAGE_HDR_ALIGN); + image_is_host_endian = TRUE; + } else { + /* safe image : little endian data */ + le32 offset_to_image_data; + int delta; + + if (image_hdr.major_ver > NTFSCLONE_IMG_VER_MAJOR) + err_exit("Do not know how to handle image format " + "version %d.%d. Please obtain a " + "newer version of ntfsclone.\n", + image_hdr.major_ver, + image_hdr.minor_ver); + /* Read the image header data offset. */ + if (read_all(&fd_in, &offset_to_image_data, + sizeof(offset_to_image_data)) == -1) + perr_exit("read_all"); + /* do not translate little endian data */ + image_hdr.offset_to_image_data = offset_to_image_data; + /* + * Read any fields from the header that we have not read yet so + * that the input stream is positioned correctly. This means + * we can support future minor versions that just extend the + * header in a backwards compatible way. + */ + delta = le32_to_cpu(offset_to_image_data) + - (NTFSCLONE_IMG_HEADER_SIZE_OLD + + sizeof(image_hdr.offset_to_image_data)); + if (delta > 0) { + char *dummy_buf; + + dummy_buf = malloc(delta); + if (!dummy_buf) + perr_exit("malloc dummy_buffer"); + if (read_all(&fd_in, dummy_buf, delta) == -1) + perr_exit("read_all"); + free(dummy_buf); + } + } + return sle64_to_cpu(image_hdr.device_size); +} + +static s64 open_volume(void) +{ + s64 device_size; + + mount_volume(NTFS_MNT_RDONLY); + + device_size = ntfs_device_size_get(vol->dev, 1); + if (device_size <= 0) + err_exit("Couldn't get device size (%lld)!\n", + (long long)device_size); + + print_volume_size("Current device size", device_size); + + if (device_size < vol->nr_clusters * vol->cluster_size) + err_exit("Current NTFS volume size is bigger than the device " + "size (%lld)!\nCorrupt partition table or incorrect " + "device partitioning?\n", (long long)device_size); + + return device_size; +} + +static void initialise_image_hdr(s64 device_size, s64 inuse) +{ + memcpy(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE); + image_hdr.major_ver = NTFSCLONE_IMG_VER_MAJOR; + image_hdr.minor_ver = NTFSCLONE_IMG_VER_MINOR; + image_hdr.cluster_size = cpu_to_le32(vol->cluster_size); + image_hdr.device_size = cpu_to_sle64(device_size); + image_hdr.nr_clusters = cpu_to_sle64(vol->nr_clusters); + image_hdr.inuse = cpu_to_sle64(inuse); + image_hdr.offset_to_image_data = cpu_to_le32((sizeof(image_hdr) + + IMAGE_HDR_ALIGN - 1) & -IMAGE_HDR_ALIGN); +} + +static void check_output_device(s64 input_size) +{ + if (opt.blkdev_out) { + s64 dest_size; + + if (dev_out) + dest_size = ntfs_device_size_get(dev_out, 1); + else + dest_size = device_size_get(fd_out); + if (dest_size < input_size) + err_exit("Output device is too small (%lld) to fit the " + "NTFS image (%lld).\n", + (long long)dest_size, (long long)input_size); + + check_if_mounted(opt.output, 0); + } else + set_filesize(input_size); +} + +static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ret; + + if ((ret = ntfs_attr_get_search_ctx(ni, NULL)) == NULL) + perr_printf("ntfs_attr_get_search_ctx"); + + return ret; +} + +/** + * lookup_data_attr + * + * Find the $DATA attribute (with or without a name) for the given ntfs inode. + */ +static ntfs_attr_search_ctx *lookup_data_attr(ntfs_inode *ni, const char *aname) +{ + ntfs_attr_search_ctx *ctx; + ntfschar *ustr; + int len = 0; + + if ((ctx = attr_get_search_ctx(ni)) == NULL) + return NULL; + + if ((ustr = ntfs_str2ucs(aname, &len)) == NULL) { + perr_printf("Couldn't convert '%s' to Unicode", aname); + goto error_out; + } + + if (ntfs_attr_lookup(AT_DATA, ustr, len, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + perr_printf("ntfs_attr_lookup"); + goto error_out; + } + ntfs_ucsfree(ustr); + return ctx; +error_out: + ntfs_attr_put_search_ctx(ctx); + return NULL; +} + +static void ignore_bad_clusters(ntfs_walk_clusters_ctx *image) +{ + ntfs_inode *ni; + ntfs_attr_search_ctx *ctx = NULL; + runlist *rl, *rl_bad; + s64 nr_bad_clusters = 0; + + if (!(ni = ntfs_inode_open(vol, FILE_BadClus))) + perr_exit("ntfs_open_inode"); + + if ((ctx = lookup_data_attr(ni, "$Bad")) == NULL) + exit(1); + + if (!(rl_bad = ntfs_mapping_pairs_decompress(vol, ctx->attr, NULL))) + perr_exit("ntfs_mapping_pairs_decompress"); + + for (rl = rl_bad; rl->length; rl++) { + s64 lcn = rl->lcn; + + if (lcn == LCN_HOLE || lcn < 0) + continue; + + for (; lcn < rl->lcn + rl->length; lcn++, nr_bad_clusters++) { + if (ntfs_bit_get_and_set(lcn_bitmap.bm, lcn, 0)) + image->inuse--; + } + } + if (nr_bad_clusters) + Printf("WARNING: The disk has %lld or more bad sectors" + " (hardware faults).\n", (long long)nr_bad_clusters); + free(rl_bad); + + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_close(ni)) + perr_exit("ntfs_inode_close failed for $BadClus"); +} + +static void check_dest_free_space(u64 src_bytes) +{ +#ifndef HAVE_WINDOWS_H + u64 dest_bytes; + struct statvfs stvfs; + struct stat st; + + if (opt.metadata || opt.blkdev_out || opt.std_out) + return; + /* + * TODO: save_image needs a bit more space than src_bytes + * due to the free space encoding overhead. + */ + if (fstatvfs(fd_out, &stvfs) == -1) { + Printf("WARNING: Unknown free space on the destination: %s\n", + strerror(errno)); + return; + } + + /* If file is a FIFO then there is no point in checking the size. */ + if (!fstat(fd_out, &st)) { + if (S_ISFIFO(st.st_mode)) + return; + } else + Printf("WARNING: fstat failed: %s\n", strerror(errno)); + + dest_bytes = (u64)stvfs.f_frsize * stvfs.f_bfree; + if (!dest_bytes) + dest_bytes = (u64)stvfs.f_bsize * stvfs.f_bfree; + + if (dest_bytes < src_bytes) + err_exit("Destination doesn't have enough free space: " + "%llu MB < %llu MB\n", + (unsigned long long)rounded_up_division(dest_bytes, NTFS_MBYTE), + (unsigned long long)rounded_up_division(src_bytes, NTFS_MBYTE)); +#endif +} + +int main(int argc, char **argv) +{ + ntfs_walk_clusters_ctx image; + s64 device_size; /* input device size in bytes */ + s64 ntfs_size; + unsigned int wiped_total = 0; + + /* make sure the layout of header is not affected by alignments */ + if (offsetof(struct image_hdr, offset_to_image_data) + != IMAGE_OFFSET_OFFSET) { + fprintf(stderr,"ntfsclone is not compiled properly. " + "Please fix\n"); + exit(1); + } + /* print to stderr, stdout can be an NTFS image ... */ + fprintf(stderr, "%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + msg_out = stderr; + + parse_options(argc, argv); + + utils_set_locale(); + + if (opt.restore_image) { + device_size = open_image(); + ntfs_size = sle64_to_cpu(image_hdr.nr_clusters) * + le32_to_cpu(image_hdr.cluster_size); + } else { + device_size = open_volume(); + ntfs_size = vol->nr_clusters * vol->cluster_size; + } + // FIXME: This needs to be the cluster size... + ntfs_size += 512; /* add backup boot sector */ + full_device_size = device_size; + + if (opt.std_out) { + if ((fd_out = fileno(stdout)) == -1) + perr_exit("fileno for stdout failed"); + stream_out = stdout; +#ifdef HAVE_WINDOWS_H + if (setmode(fileno(stdout),O_BINARY) == -1) + perr_exit("setting binary stdout failed"); +#endif + } else { + /* device_size_get() might need to read() */ + int flags = O_RDWR | O_BINARY; + + fd_out = 0; + if (!opt.blkdev_out) { + flags |= O_CREAT | O_TRUNC; + if (!opt.overwrite) + flags |= O_EXCL; + } + + if (opt.save_image || opt.metadata_image) { + stream_out = fopen(opt.output,BINWMODE); + if (!stream_out) + perr_exit("Opening file '%s' failed", + opt.output); + fd_out = fileno(stream_out); + } else { +#ifdef HAVE_WINDOWS_H + if (!opt.no_action) { + dev_out = ntfs_device_alloc(opt.output, 0, + &ntfs_device_default_io_ops, NULL); + if (!dev_out + || (dev_out->d_ops->open)(dev_out, flags)) + perr_exit("Opening volume '%s' failed", + opt.output); + } +#else + if (!opt.no_action + && ((fd_out = open(opt.output, flags, + S_IRUSR | S_IWUSR)) == -1)) + perr_exit("Opening file '%s' failed", + opt.output); +#endif + } + + if (!opt.save_image && !opt.metadata_image && !opt.no_action) + check_output_device(ntfs_size); + } + + if (opt.restore_image) { + print_image_info(); + restore_image(); + if (!opt.no_action) + fsync_clone(fd_out); + exit(0); + } + + setup_lcn_bitmap(); + memset(&image, 0, sizeof(image)); + backup_clusters.image = ℑ + + walk_clusters(vol, &backup_clusters); + image.more_use = compare_bitmaps(&lcn_bitmap, + opt.metadata && !opt.metadata_image); + print_disk_usage("", vol->cluster_size, vol->nr_clusters, image.inuse); + + check_dest_free_space(vol->cluster_size * image.inuse); + + ignore_bad_clusters(&image); + + if (opt.save_image) + initialise_image_hdr(device_size, image.inuse); + + if ((opt.std_out && !opt.metadata_image) || !opt.metadata) { + s64 nr_clusters_to_save = image.inuse; + if (opt.std_out && !opt.save_image) + nr_clusters_to_save = vol->nr_clusters; + nr_clusters_to_save++; /* account for the backup boot sector */ + + clone_ntfs(nr_clusters_to_save, image.more_use); + fsync_clone(fd_out); + if (opt.save_image) + fclose(stream_out); + ntfs_umount(vol,FALSE); + free(lcn_bitmap.bm); + exit(0); + } + + wipe = 1; + if (opt.metadata_image) { + initialise_image_hdr(device_size, image.inuse); + write_image_hdr(); + } else { + if (dev_out) { + (dev_out->d_ops->close)(dev_out); + dev_out = NULL; + } else + fsync_clone(fd_out); /* sync copy before mounting */ + opt.volume = opt.output; + /* 'force' again mount for dirty volumes (e.g. after resize). + FIXME: use mount flags to avoid potential side-effects in future */ + opt.force++; + ntfs_umount(vol,FALSE); + mount_volume(0 /*NTFS_MNT_NOATIME*/); + } + + free(lcn_bitmap.bm); + setup_lcn_bitmap(); + memset(&image, 0, sizeof(image)); + backup_clusters.image = ℑ + + walk_clusters(vol, &backup_clusters); + + Printf("Num of MFT records = %10lld\n", + (long long)vol->mft_na->initialized_size >> + vol->mft_record_size_bits); + Printf("Num of used MFT records = %10u\n", nr_used_mft_records); + + Printf("Wiped unused MFT data = %10u\n", wiped_unused_mft_data); + Printf("Wiped deleted MFT data = %10u\n", wiped_unused_mft); + Printf("Wiped resident user data = %10u\n", wiped_resident_data); + Printf("Wiped timestamp data = %10u\n", wiped_timestamp_data); + + wiped_total += wiped_unused_mft_data; + wiped_total += wiped_unused_mft; + wiped_total += wiped_resident_data; + wiped_total += wiped_timestamp_data; + Printf("Wiped totally = %10u\n", wiped_total); + + if (opt.metadata_image) + fclose(stream_out); + else + fsync_clone(fd_out); + ntfs_umount(vol,FALSE); + free(lcn_bitmap.bm); + return (0); +} diff --git a/ntfsprogs/ntfscluster.8 b/ntfsprogs/ntfscluster.8 new file mode 100755 index 0000000000000000000000000000000000000000..7fc537b5d63001bb0476b8070b8368f546aa094d --- /dev/null +++ b/ntfsprogs/ntfscluster.8 @@ -0,0 +1,124 @@ +.\" Copyright (c) 2003\-2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCLUSTER 8 "November 2005" "ntfs-3g 2015.3.14" +.SH NAME +ntfscluster \- identify files in a specified region of an NTFS volume. +.SH SYNOPSIS +.B ntfscluster +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfscluster +has three modes of operation: +.IR info , +.I sector +and +.IR cluster . +.SS Info +.PP +The default mode, +.I info +is currently not implemented. It will display general information about the +NTFS volume when it is working. +.SS Sector +.PP +The +.I sector +mode will display a list of files that have data in the specified range of +sectors. +.SS Cluster +The +.I cluster +mode will display a list of files that have data in the specified range of +clusters. When the cluster size is one sector, this will be equivalent to the +.I sector +mode of operation. +.SH OPTIONS +Below is a summary of all the options that +.B ntfscluster +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-c\fR, \fB\-\-cluster\fR RANGE +Any files whose data is in this range of clusters will be displayed. +.TP +\fB\-F\fR, \fB\-\-filename\fR NAME +Show information about this file. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not working with a mounted +volume. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-I\fR, \fB\-\-inode\fR NUM +Show information about this inode. +.TP +\fB\-i\fR, \fB\-\-info\fR +This option is not yet implemented. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Reduce the amount of output to a minimum. Naturally, it doesn't make sense to +combine this option with +.TP +\fB\-s\fR, \fB\-\-sector\fR RANGE +Any files whose data is in this range of sectors will be displayed. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfscluster +prints. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license for +.BR ntfscluster . +.SH EXAMPLES +Get some information about the volume /dev/hda1. +.RS +.sp +.B ntfscluster /dev/hda1 +.sp +.RE +Look for files in the first 500 clusters of /dev/hda1. +.RS +.sp +.B ntfscluster \-c 0\-500 /dev/hda1 +.sp +.RE +.SH BUGS +The +.I info +mode isn't implemented yet. +.B ntfscluster +is quite limited, but it has no known bugs. If you find a bug please send an +email describing the problem to the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfscluster +was written by Richard Russon, with contributions from Anton Altaparmakov. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfscluster +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsinfo (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscluster.8.in b/ntfsprogs/ntfscluster.8.in new file mode 100755 index 0000000000000000000000000000000000000000..9186a64872456b1399bf7de48d6b4ea3a07c0398 --- /dev/null +++ b/ntfsprogs/ntfscluster.8.in @@ -0,0 +1,124 @@ +.\" Copyright (c) 2003\-2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCLUSTER 8 "November 2005" "ntfs-3g @VERSION@" +.SH NAME +ntfscluster \- identify files in a specified region of an NTFS volume. +.SH SYNOPSIS +.B ntfscluster +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfscluster +has three modes of operation: +.IR info , +.I sector +and +.IR cluster . +.SS Info +.PP +The default mode, +.I info +is currently not implemented. It will display general information about the +NTFS volume when it is working. +.SS Sector +.PP +The +.I sector +mode will display a list of files that have data in the specified range of +sectors. +.SS Cluster +The +.I cluster +mode will display a list of files that have data in the specified range of +clusters. When the cluster size is one sector, this will be equivalent to the +.I sector +mode of operation. +.SH OPTIONS +Below is a summary of all the options that +.B ntfscluster +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-c\fR, \fB\-\-cluster\fR RANGE +Any files whose data is in this range of clusters will be displayed. +.TP +\fB\-F\fR, \fB\-\-filename\fR NAME +Show information about this file. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not working with a mounted +volume. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-I\fR, \fB\-\-inode\fR NUM +Show information about this inode. +.TP +\fB\-i\fR, \fB\-\-info\fR +This option is not yet implemented. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Reduce the amount of output to a minimum. Naturally, it doesn't make sense to +combine this option with +.TP +\fB\-s\fR, \fB\-\-sector\fR RANGE +Any files whose data is in this range of sectors will be displayed. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfscluster +prints. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license for +.BR ntfscluster . +.SH EXAMPLES +Get some information about the volume /dev/hda1. +.RS +.sp +.B ntfscluster /dev/hda1 +.sp +.RE +Look for files in the first 500 clusters of /dev/hda1. +.RS +.sp +.B ntfscluster \-c 0\-500 /dev/hda1 +.sp +.RE +.SH BUGS +The +.I info +mode isn't implemented yet. +.B ntfscluster +is quite limited, but it has no known bugs. If you find a bug please send an +email describing the problem to the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfscluster +was written by Richard Russon, with contributions from Anton Altaparmakov. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfscluster +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsinfo (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscluster.c b/ntfsprogs/ntfscluster.c new file mode 100755 index 0000000000000000000000000000000000000000..6a7e72897e3f7640c0882abdec7335f5d4b6eda5 --- /dev/null +++ b/ntfsprogs/ntfscluster.c @@ -0,0 +1,567 @@ +/** + * ntfscluster - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2003 Richard Russon + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * + * This utility will locate the owner of any given sector or cluster. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include "ntfscluster.h" +#include "types.h" +#include "attrib.h" +#include "utils.h" +#include "volume.h" +#include "debug.h" +#include "dir.h" +#include "cluster.h" +/* #include "version.h" */ +#include "logging.h" + +static const char *EXEC_NAME = "ntfscluster"; +static struct options opts; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Find the owner of any given sector or " + "cluster.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2002-2003 Richard Russon\n"); + ntfs_log_info("Copyright (c) 2005 Anton Altaparmakov\n"); + ntfs_log_info("Copyright (c) 2005-2006 Szabolcs Szakacsits\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device\n" + " -i, --info Print information about the volume (default)\n" + "\n" + " -c, --cluster RANGE Look for objects in this range of clusters\n" + " -s, --sector RANGE Look for objects in this range of sectors\n" + " -I, --inode NUM Show information about this inode\n" + " -F, --filename NAME Show information about this file\n" + /* " -l, --last Find the last file on the volume\n" */ + "\n" + " -f, --force Use less caution\n" + " -q, --quiet Less output\n" + " -v, --verbose More output\n" + " -V, --version Version information\n" + " -h, --help Print this help\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-c:F:fh?I:ilqs:vV"; + static const struct option lopt[] = { + { "cluster", required_argument, NULL, 'c' }, + { "filename", required_argument, NULL, 'F' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "info", no_argument, NULL, 'i' }, + { "inode", required_argument, NULL, 'I' }, + { "last", no_argument, NULL, 'l' }, + { "quiet", no_argument, NULL, 'q' }, + { "sector", required_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + char *end = NULL; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.action = act_none; + opts.range_begin = -1; + opts.range_end = -1; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind-1]; + } else { + opts.device = NULL; + err++; + } + break; + + case 'c': + if ((opts.action == act_none) && + (utils_parse_range(optarg, &opts.range_begin, &opts.range_end, FALSE))) + opts.action = act_cluster; + else + opts.action = act_error; + break; + case 'F': + if (opts.action == act_none) { + opts.action = act_file; + opts.filename = optarg; + } else { + opts.action = act_error; + } + break; + case 'f': + opts.force++; + break; + case 'h': + help++; + break; + case 'I': + if (opts.action == act_none) { + opts.action = act_inode; + opts.inode = strtol(optarg, &end, 0); + if (end && *end) + err++; + } else { + opts.action = act_error; + } + break; + case 'i': + if (opts.action == act_none) + opts.action = act_info; + else + opts.action = act_error; + break; + case 'l': + if (opts.action == act_none) + opts.action = act_last; + else + opts.action = act_error; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 's': + if ((opts.action == act_none) && + (utils_parse_range(optarg, &opts.range_begin, &opts.range_end, FALSE))) + opts.action = act_sector; + else + opts.action = act_error; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + /* fall through */ + default: + if ((optopt == 'c') || (optopt == 's')) + ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]); + else + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.action == act_none) + opts.action = act_info; + if (opts.action == act_info) + opts.quiet = 0; + + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify exactly one device.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the same time.\n"); + err++; + } + + if (opts.action == act_error) { + ntfs_log_error("You may only specify one action: --info, --cluster, --sector or --last.\n"); + err++; + } else if (opts.range_begin > opts.range_end) { + ntfs_log_error("The range must be in ascending order.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + /* tri-state 0 : done, 1 : error, -1 : proceed */ + return (err ? 1 : (help || ver ? 0 : -1)); +} + + +/** + * info + */ +static int info(ntfs_volume *vol) +{ + u64 a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u; + int cb, sb, cps; + u64 uc = 0, mc = 0, fc = 0; + + struct mft_search_ctx *m_ctx; + ntfs_attr_search_ctx *a_ctx; + runlist_element *rl; + ATTR_RECORD *rec; + int z; + int inuse = 0; + + m_ctx = mft_get_search_ctx(vol); + m_ctx->flags_search = FEMR_IN_USE | FEMR_METADATA | FEMR_BASE_RECORD | FEMR_NOT_BASE_RECORD; + while (mft_next_record(m_ctx) == 0) { + + if (!(m_ctx->flags_match & FEMR_IN_USE)) + continue; + + inuse++; + + a_ctx = ntfs_attr_get_search_ctx(m_ctx->inode, NULL); + + while ((rec = find_attribute(AT_UNUSED, a_ctx))) { + + if (!rec->non_resident) + continue; + + rl = ntfs_mapping_pairs_decompress(vol, rec, NULL); + + for (z = 0; rl[z].length > 0; z++) + { + if (rl[z].lcn >= 0) { + if (m_ctx->flags_match & FEMR_METADATA) + mc += rl[z].length; + else + uc += rl[z].length; + } + + } + + free(rl); + } + + ntfs_attr_put_search_ctx(a_ctx); + } + mft_put_search_ctx(m_ctx); + + cb = vol->cluster_size_bits; + sb = vol->sector_size_bits; + cps = cb - sb; + + fc = vol->nr_clusters-mc-uc; + fc <<= cb; + mc <<= cb; + uc <<= cb; + + a = vol->sector_size; + b = vol->cluster_size; + c = 1 << cps; + d = vol->nr_clusters << cb; + e = vol->nr_clusters; + f = vol->nr_clusters >> cps; + g = vol->mft_na->initialized_size >> vol->mft_record_size_bits; + h = inuse; + i = h * 100 / g; + j = fc; + k = fc >> sb; + l = fc >> cb; + m = fc * 100 / b / e; + n = uc; + o = uc >> sb; + p = uc >> cb; + q = uc * 100 / b / e; + r = mc; + s = mc >> sb; + t = mc >> cb; + u = mc * 100 / b / e; + + ntfs_log_info("bytes per sector : %llu\n", (unsigned long long)a); + ntfs_log_info("bytes per cluster : %llu\n", (unsigned long long)b); + ntfs_log_info("sectors per cluster : %llu\n", (unsigned long long)c); + ntfs_log_info("bytes per volume : %llu\n", (unsigned long long)d); + ntfs_log_info("sectors per volume : %llu\n", (unsigned long long)e); + ntfs_log_info("clusters per volume : %llu\n", (unsigned long long)f); + ntfs_log_info("initialized mft records : %llu\n", (unsigned long long)g); + ntfs_log_info("mft records in use : %llu\n", (unsigned long long)h); + ntfs_log_info("mft records percentage : %llu\n", (unsigned long long)i); + ntfs_log_info("bytes of free space : %llu\n", (unsigned long long)j); + ntfs_log_info("sectors of free space : %llu\n", (unsigned long long)k); + ntfs_log_info("clusters of free space : %llu\n", (unsigned long long)l); + ntfs_log_info("percentage free space : %llu\n", (unsigned long long)m); + ntfs_log_info("bytes of user data : %llu\n", (unsigned long long)n); + ntfs_log_info("sectors of user data : %llu\n", (unsigned long long)o); + ntfs_log_info("clusters of user data : %llu\n", (unsigned long long)p); + ntfs_log_info("percentage user data : %llu\n", (unsigned long long)q); + ntfs_log_info("bytes of metadata : %llu\n", (unsigned long long)r); + ntfs_log_info("sectors of metadata : %llu\n", (unsigned long long)s); + ntfs_log_info("clusters of metadata : %llu\n", (unsigned long long)t); + ntfs_log_info("percentage metadata : %llu\n", (unsigned long long)u); + + return 0; +} + +/** + * dump_file + */ +static int dump_file(ntfs_volume *vol, ntfs_inode *ino) +{ + char buffer[1024]; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *rec; + int i; + runlist *runs; + + utils_inode_get_name(ino, buffer, sizeof(buffer)); + + ntfs_log_info("Dump: %s\n", buffer); + + ctx = ntfs_attr_get_search_ctx(ino, NULL); + + while ((rec = find_attribute(AT_UNUSED, ctx))) { + ntfs_log_info(" 0x%02x - ", (int)le32_to_cpu(rec->type)); + if (rec->non_resident) { + ntfs_log_info("non-resident\n"); + runs = ntfs_mapping_pairs_decompress(vol, rec, NULL); + if (runs) { + ntfs_log_info(" VCN LCN Length\n"); + for (i = 0; runs[i].length > 0; i++) { + ntfs_log_info(" %8lld %8lld %8lld\n", + (long long)runs[i].vcn, + (long long)runs[i].lcn, + (long long) + runs[i].length); + } + free(runs); + } + } else { + ntfs_log_info("resident\n"); + } + } + + ntfs_attr_put_search_ctx(ctx); + return 0; +} + +/** + * print_match + */ +static int print_match(ntfs_inode *ino, ATTR_RECORD *attr, + runlist_element *run, void *data __attribute__((unused))) +{ + char *buffer; + + if (!ino || !attr || !run) + return 1; + + buffer = malloc(MAX_PATH); + if (!buffer) { + ntfs_log_error("!buffer\n"); + return 1; + } + + utils_inode_get_name(ino, buffer, MAX_PATH); + ntfs_log_info("Inode %llu %s", (unsigned long long)ino->mft_no, buffer); + + utils_attr_get_name(ino->vol, attr, buffer, MAX_PATH); + ntfs_log_info("/%s\n", buffer); + + free(buffer); + return 0; +} + +/** + * find_last + */ +static int find_last(ntfs_inode *ino, ATTR_RECORD *attr, runlist_element *run, + void *data) +{ + struct match *m; + + if (!ino || !attr || !run || !data) + return 1; + + m = data; + + if ((run->lcn + run->length) > m->lcn) { + m->inum = ino->mft_no; + m->lcn = run->lcn + run->length; + } + + return 0; +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + ntfs_volume *vol; + ntfs_inode *ino = NULL; + struct match m; + int res; + int result = 1; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + res = parse_options(argc, argv); + if (res >= 0) + return (res); + + utils_set_locale(); + + vol = utils_mount_volume(opts.device, NTFS_MNT_RDONLY | + (opts.force ? NTFS_MNT_RECOVER : 0)); + if (!vol) + return 1; + + switch (opts.action) { + case act_sector: + if (opts.range_begin == opts.range_end) + ntfs_log_quiet("Searching for sector %llu\n", + (unsigned long long)opts.range_begin); + else + ntfs_log_quiet("Searching for sector range %llu-%llu\n", (unsigned long long)opts.range_begin, (unsigned long long)opts.range_end); + /* Convert to clusters */ + opts.range_begin >>= (vol->cluster_size_bits - vol->sector_size_bits); + opts.range_end >>= (vol->cluster_size_bits - vol->sector_size_bits); + result = cluster_find(vol, opts.range_begin, opts.range_end, (cluster_cb*)&print_match, NULL); + break; + case act_cluster: + if (opts.range_begin == opts.range_end) + ntfs_log_quiet("Searching for cluster %llu\n", + (unsigned long long)opts.range_begin); + else + ntfs_log_quiet("Searching for cluster range %llu-%llu\n", (unsigned long long)opts.range_begin, (unsigned long long)opts.range_end); + result = cluster_find(vol, opts.range_begin, opts.range_end, (cluster_cb*)&print_match, NULL); + break; + case act_file: + ino = ntfs_pathname_to_inode(vol, NULL, opts.filename); + if (ino) + result = dump_file(vol, ino); + break; + case act_inode: + ino = ntfs_inode_open(vol, opts.inode); + if (ino) { + result = dump_file(vol, ino); + ntfs_inode_close(ino); + } else { + ntfs_log_error("Cannot open inode %llu\n", + (unsigned long long)opts.inode); + } + break; + case act_last: + memset(&m, 0, sizeof(m)); + m.lcn = -1; + result = cluster_find(vol, 0, LONG_MAX, (cluster_cb*)&find_last, &m); + if (m.lcn >= 0) { + ino = ntfs_inode_open(vol, m.inum); + if (ino) { + result = dump_file(vol, ino); + ntfs_inode_close(ino); + } else { + ntfs_log_error("Cannot open inode %llu\n", + (unsigned long long) + opts.inode); + } + result = 0; + } else { + result = 1; + } + break; + case act_info: + default: + result = info(vol); + break; + } + + ntfs_umount(vol, FALSE); + return result; +} + + diff --git a/ntfsprogs/ntfscluster.h b/ntfsprogs/ntfscluster.h new file mode 100755 index 0000000000000000000000000000000000000000..5a3d6b3c0483460c2dd1a3f386af5ee2e755cc06 --- /dev/null +++ b/ntfsprogs/ntfscluster.h @@ -0,0 +1,63 @@ +/* + * ntfscluster - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2003 Richard Russon + * + * This utility will locate the owner of any given sector or cluster. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFSCLUSTER_H_ +#define _NTFSCLUSTER_H_ + +#include "types.h" +#include "layout.h" + +enum action { + act_none, + act_info, + act_cluster, + act_sector, + act_inode, + act_file, + act_last, + act_error, +}; + +struct options { + char *device; /* Device/File to work with */ + enum action action; /* What to do */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int force; /* Override common sense */ + char *filename; /* File to examine */ + u64 inode; /* Inode to examine */ + s64 range_begin; /* Look for objects in this range */ + s64 range_end; +}; + +struct match { + u64 inum; /* Inode number */ + LCN lcn; /* Last cluster in use */ + ATTR_TYPES type; /* Attribute type */ + ntfschar *name; /* Attribute name */ + int name_len; /* Length of attribute name */ +}; + +#endif /* _NTFSCLUSTER_H_ */ + + diff --git a/ntfsprogs/ntfscmp.8 b/ntfsprogs/ntfscmp.8 new file mode 100755 index 0000000000000000000000000000000000000000..1686fc4fa0ae1de5cdac1144c8beb0c87fc1b2df --- /dev/null +++ b/ntfsprogs/ntfscmp.8 @@ -0,0 +1,77 @@ +.\" Copyright (c) 2005\-2006 Szabolcs Szakacsits. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCMP 8 "April 2006" "ntfs-3g 2015.3.14" +.SH NAME +ntfscmp \- compare two NTFS filesystems and tell the differences +.SH SYNOPSIS +.B ntfscmp +[\fIOPTIONS\fR] +.I DEVICE1 +.I DEVICE2 +.br +.SH DESCRIPTION +The +.B ntfscmp +program makes a comparison between two NTFS filesystems from all aspects and +reports all variances it finds. +The filesystems can be on block devices or images files. Ntfscmp can be used +for volume verification however its primary purpose was to be an efficient +development tool, used to quickly locate, identify and check the correctness +of the metadata changes made to NTFS. + +If one is interested only in the NTFS metadata changes then it could be useful +to compare the metadata images created by +using the --metadata option of +.BR ntfsclone (8) +to eliminate the usually uninteresting timestamp changes. + +The terse output of +.B ntfscmp +is intentional because the provided information is enough in each case +to determine the exact differences. This can be achieved, for instance, +if one compares the verbose outputs of +.BR ntfsinfo (8) +for each reported inodes by the +.BR diff (1) +utility. +.SH OPTIONS +Below is a summary of the options that +.B ntfscmp +accepts. +.TP +\fB\-P\fR, \fB\-\-no\-progress\-bar\fR +Don't show progress bars. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +More informational output. +.TP +\fB\-h\fR, \fB\-\-help\fR +Display help and exit. +.SH EXIT CODES +The exit code is 0 on success, non\-zero otherwise. +.SH KNOWN ISSUES +No problem is known. If you would find otherwise then please send +your report to the development team: +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHOR +.B ntfscmp +was written by Szabolcs Szakacsits. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfscmp +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsinfo (8), +.BR ntfscat (8), +.BR diff (1), +.BR ntfsclone (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscmp.8.in b/ntfsprogs/ntfscmp.8.in new file mode 100755 index 0000000000000000000000000000000000000000..647cf5449ac4b4415e7e0624f978f1689f5eb28d --- /dev/null +++ b/ntfsprogs/ntfscmp.8.in @@ -0,0 +1,77 @@ +.\" Copyright (c) 2005\-2006 Szabolcs Szakacsits. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCMP 8 "April 2006" "ntfs-3g @VERSION@" +.SH NAME +ntfscmp \- compare two NTFS filesystems and tell the differences +.SH SYNOPSIS +.B ntfscmp +[\fIOPTIONS\fR] +.I DEVICE1 +.I DEVICE2 +.br +.SH DESCRIPTION +The +.B ntfscmp +program makes a comparison between two NTFS filesystems from all aspects and +reports all variances it finds. +The filesystems can be on block devices or images files. Ntfscmp can be used +for volume verification however its primary purpose was to be an efficient +development tool, used to quickly locate, identify and check the correctness +of the metadata changes made to NTFS. + +If one is interested only in the NTFS metadata changes then it could be useful +to compare the metadata images created by +using the --metadata option of +.BR ntfsclone (8) +to eliminate the usually uninteresting timestamp changes. + +The terse output of +.B ntfscmp +is intentional because the provided information is enough in each case +to determine the exact differences. This can be achieved, for instance, +if one compares the verbose outputs of +.BR ntfsinfo (8) +for each reported inodes by the +.BR diff (1) +utility. +.SH OPTIONS +Below is a summary of the options that +.B ntfscmp +accepts. +.TP +\fB\-P\fR, \fB\-\-no\-progress\-bar\fR +Don't show progress bars. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +More informational output. +.TP +\fB\-h\fR, \fB\-\-help\fR +Display help and exit. +.SH EXIT CODES +The exit code is 0 on success, non\-zero otherwise. +.SH KNOWN ISSUES +No problem is known. If you would find otherwise then please send +your report to the development team: +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHOR +.B ntfscmp +was written by Szabolcs Szakacsits. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfscmp +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsinfo (8), +.BR ntfscat (8), +.BR diff (1), +.BR ntfsclone (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscmp.c b/ntfsprogs/ntfscmp.c new file mode 100755 index 0000000000000000000000000000000000000000..469d1d9a4a19628976bcd6c066033c5de4b7541c --- /dev/null +++ b/ntfsprogs/ntfscmp.c @@ -0,0 +1,1012 @@ +/** + * ntfscmp - Part of the Linux-NTFS project. + * + * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2007 Yura Pakhuchiy + * + * This utility compare two NTFS volumes. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> + +#include "mst.h" +#include "support.h" +#include "utils.h" +#include "misc.h" +/* #include "version.h" */ + +static const char *EXEC_NAME = "ntfscmp"; + +static const char *invalid_ntfs_msg = +"Apparently device '%s' doesn't have a valid NTFS.\n" +"Maybe you selected the wrong partition? Or the whole disk instead of a\n" +"partition (e.g. /dev/hda, not /dev/hda1)?\n"; + +static const char *corrupt_volume_msg = +"Apparently you have a corrupted NTFS. Please run the filesystem checker\n" +"on Windows by invoking chkdsk /f. Don't forget the /f (force) parameter,\n" +"it's important! You probably also need to reboot Windows to take effect.\n"; + +static const char *hibernated_volume_msg = +"Apparently the NTFS partition is hibernated. Windows must be resumed and\n" +"turned off properly\n"; + + +static struct { + int debug; + int show_progress; + int verbose; + char *vol1; + char *vol2; +} opt; + + +#define NTFS_PROGBAR 0x0001 +#define NTFS_PROGBAR_SUPPRESS 0x0002 + +struct progress_bar { + u64 start; + u64 stop; + int resolution; + int flags; + float unit; +}; + +/* WARNING: don't modify the text, external tools grep for it */ +#define ERR_PREFIX "ERROR" +#define PERR_PREFIX ERR_PREFIX "(%d): " +#define NERR_PREFIX ERR_PREFIX ": " + +__attribute__((format(printf, 2, 3))) +static void perr_printf(int newline, const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + fprintf(stdout, PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fprintf(stdout, ": %s", strerror(eo)); + if (newline) + fprintf(stdout, "\n"); + fflush(stdout); + fflush(stderr); +} + +#define perr_print(...) perr_printf(0, __VA_ARGS__) +#define perr_println(...) perr_printf(1, __VA_ARGS__) + +__attribute__((format(printf, 1, 2))) +static void err_printf(const char *fmt, ...) +{ + va_list ap; + + fprintf(stdout, NERR_PREFIX); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fflush(stdout); + fflush(stderr); +} + +/** + * err_exit + * + * Print and error message and exit the program. + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static int err_exit(const char *fmt, ...) +{ + va_list ap; + + fprintf(stdout, NERR_PREFIX); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fflush(stdout); + fflush(stderr); + exit(1); +} + +/** + * perr_exit + * + * Print and error message and exit the program + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static int perr_exit(const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + fprintf(stdout, PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + printf(": %s\n", strerror(eo)); + fflush(stdout); + fflush(stderr); + exit(1); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +__attribute__((noreturn)) +static void usage(void) +{ + + printf("\nUsage: %s [OPTIONS] DEVICE1 DEVICE2\n" + " Compare two NTFS volumes and tell the differences.\n" + "\n" + " -P, --no-progress-bar Don't show progress bar\n" + " -v, --verbose More output\n" + " -h, --help Display this help\n" +#ifdef DEBUG + " -d, --debug Show debug information\n" +#endif + "\n", EXEC_NAME); + printf("%s%s", ntfs_bugs, ntfs_home); + exit(1); +} + + +static void parse_options(int argc, char **argv) +{ + static const char *sopt = "-dhPv"; + static const struct option lopt[] = { +#ifdef DEBUG + { "debug", no_argument, NULL, 'd' }, +#endif + { "help", no_argument, NULL, 'h' }, + { "no-progress-bar", no_argument, NULL, 'P' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + memset(&opt, 0, sizeof(opt)); + opt.show_progress = 1; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opt.vol1) { + opt.vol1 = argv[optind - 1]; + } else if (!opt.vol2) { + opt.vol2 = argv[optind - 1]; + } else { + err_printf("Too many arguments!\n"); + usage(); + } + break; +#ifdef DEBUG + case 'd': + opt.debug++; + break; +#endif + case 'h': + case '?': + usage(); + case 'P': + opt.show_progress = 0; + break; + case 'v': + opt.verbose++; + break; + default: + err_printf("Unknown option '%s'.\n", argv[optind - 1]); + usage(); + break; + } + } + + if (opt.vol1 == NULL || opt.vol2 == NULL) { + err_printf("You must specify exactly 2 volumes.\n"); + usage(); + } + + /* Redirect stderr to stdout, note fflush()es are essential! */ + fflush(stdout); + fflush(stderr); + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) { + perror("Failed to redirect stderr to stdout"); + exit(1); + } + fflush(stdout); + fflush(stderr); + +#ifdef DEBUG + if (!opt.debug) + if (!freopen("/dev/null", "w", stderr)) + perr_exit("Failed to redirect stderr to /dev/null"); +#endif +} + +static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ret; + + if ((ret = ntfs_attr_get_search_ctx(ni, NULL)) == NULL) + perr_println("ntfs_attr_get_search_ctx"); + + return ret; +} + +static void progress_init(struct progress_bar *p, u64 start, u64 stop, int flags) +{ + p->start = start; + p->stop = stop; + p->unit = 100.0 / (stop - start); + p->resolution = 100; + p->flags = flags; +} + +static void progress_update(struct progress_bar *p, u64 current) +{ + float percent; + + if (!(p->flags & NTFS_PROGBAR)) + return; + if (p->flags & NTFS_PROGBAR_SUPPRESS) + return; + + /* WARNING: don't modify the texts, external tools grep for them */ + percent = p->unit * current; + if (current != p->stop) { + if ((current - p->start) % p->resolution) + return; + printf("%6.2f percent completed\r", percent); + } else + printf("100.00 percent completed\n"); + fflush(stdout); +} + +static u64 inumber(ntfs_inode *ni) +{ + if (ni->nr_extents >= 0) + return ni->mft_no; + + return ni->base_ni->mft_no; +} + +static int inode_close(ntfs_inode *ni) +{ + if (ni == NULL) + return 0; + + if (ntfs_inode_close(ni)) { + perr_println("ntfs_inode_close: inode %llu", + (unsigned long long)inumber(ni)); + return -1; + } + return 0; +} + +static inline s64 get_nr_mft_records(ntfs_volume *vol) +{ + return vol->mft_na->initialized_size >> vol->mft_record_size_bits; +} + +#define NTFSCMP_OK 0 +#define NTFSCMP_INODE_OPEN_ERROR 1 +#define NTFSCMP_INODE_OPEN_IO_ERROR 2 +#define NTFSCMP_INODE_OPEN_ENOENT_ERROR 3 +#define NTFSCMP_EXTENSION_RECORD 4 +#define NTFSCMP_INODE_CLOSE_ERROR 5 + +static const char *ntfscmp_errs[] = { + "OK", + "INODE_OPEN_ERROR", + "INODE_OPEN_IO_ERROR", + "INODE_OPEN_ENOENT_ERROR", + "EXTENSION_RECORD", + "INODE_CLOSE_ERROR", + "" +}; + + +static const char *err2string(int err) +{ + return ntfscmp_errs[err]; +} + +static const char *pret2str(void *p) +{ + if (p == NULL) + return "FAILED"; + return "OK"; +} + +static int inode_open(ntfs_volume *vol, MFT_REF mref, ntfs_inode **ni) +{ + *ni = ntfs_inode_open(vol, mref); + if (*ni == NULL) { + if (errno == EIO) + return NTFSCMP_INODE_OPEN_IO_ERROR; + if (errno == ENOENT) + return NTFSCMP_INODE_OPEN_ENOENT_ERROR; + + perr_println("Reading inode %lld failed", (long long)mref); + return NTFSCMP_INODE_OPEN_ERROR; + } + + if ((*ni)->mrec->base_mft_record) { + + if (inode_close(*ni) != 0) + return NTFSCMP_INODE_CLOSE_ERROR; + + return NTFSCMP_EXTENSION_RECORD; + } + + return NTFSCMP_OK; +} + +static ntfs_inode *base_inode(ntfs_attr_search_ctx *ctx) +{ + if (ctx->base_ntfs_ino) + return ctx->base_ntfs_ino; + + return ctx->ntfs_ino; +} + +static void print_inode(u64 inum) +{ + printf("Inode %llu ", (unsigned long long)inum); +} + +static void print_inode_ni(ntfs_inode *ni) +{ + print_inode(inumber(ni)); +} + +static void print_attribute_type(ATTR_TYPES atype) +{ + printf("attribute 0x%x", atype); +} + +static void print_attribute_name(char *name) +{ + if (name) + printf(":%s", name); +} + +#define GET_ATTR_NAME(a) \ + ((ntfschar *)(((u8 *)(a)) + le16_to_cpu((a)->name_offset))), \ + ((a)->name_length) + +static void free_name(char **name) +{ + if (*name) { + free(*name); + *name = NULL; + } +} + +static char *get_attr_name(u64 mft_no, + ATTR_TYPES atype, + const ntfschar *uname, + const int uname_len) +{ + char *name = NULL; + int name_len; + + if (atype == AT_END) + return NULL; + + name_len = ntfs_ucstombs(uname, uname_len, &name, 0); + if (name_len < 0) { + perr_print("ntfs_ucstombs"); + print_inode(mft_no); + print_attribute_type(atype); + puts(""); + exit(1); + + } else if (name_len > 0) + return name; + + free_name(&name); + return NULL; +} + +static char *get_attr_name_na(ntfs_attr *na) +{ + return get_attr_name(inumber(na->ni), na->type, na->name, na->name_len); +} + +static char *get_attr_name_ctx(ntfs_attr_search_ctx *ctx) +{ + u64 mft_no = inumber(ctx->ntfs_ino); + ATTR_TYPES atype = ctx->attr->type; + + return get_attr_name(mft_no, atype, GET_ATTR_NAME(ctx->attr)); +} + +static void print_attribute(ATTR_TYPES atype, char *name) +{ + print_attribute_type(atype); + print_attribute_name(name); + printf(" "); +} + +static void print_na(ntfs_attr *na) +{ + char *name = get_attr_name_na(na); + print_inode_ni(na->ni); + print_attribute(na->type, name); + free_name(&name); +} + +static void print_attribute_ctx(ntfs_attr_search_ctx *ctx) +{ + char *name = get_attr_name_ctx(ctx); + print_attribute(ctx->attr->type, name); + free_name(&name); +} + +static void print_ctx(ntfs_attr_search_ctx *ctx) +{ + char *name = get_attr_name_ctx(ctx); + print_inode_ni(base_inode(ctx)); + print_attribute(ctx->attr->type, name); + free_name(&name); +} + +static void print_differ(ntfs_attr *na) +{ + print_na(na); + printf("content: DIFFER\n"); +} + +static int cmp_buffer(u8 *buf1, u8 *buf2, long long int size, ntfs_attr *na) +{ + if (memcmp(buf1, buf2, size)) { + print_differ(na); + return -1; + } + return 0; +} + +struct cmp_ia { + INDEX_ALLOCATION *ia; + INDEX_ALLOCATION *tmp_ia; + u8 *bitmap; + u8 *byte; + s64 bm_size; +}; + +static int setup_cmp_ia(ntfs_attr *na, struct cmp_ia *cia) +{ + cia->bitmap = ntfs_attr_readall(na->ni, AT_BITMAP, na->name, + na->name_len, &cia->bm_size); + if (!cia->bitmap) { + perr_println("Failed to readall BITMAP"); + return -1; + } + cia->byte = cia->bitmap; + + cia->tmp_ia = cia->ia = ntfs_malloc(na->data_size); + if (!cia->tmp_ia) + goto free_bm; + + if (ntfs_attr_pread(na, 0, na->data_size, cia->ia) != na->data_size) { + perr_println("Failed to pread INDEX_ALLOCATION"); + goto free_ia; + } + + return 0; +free_ia: + free(cia->ia); +free_bm: + free(cia->bitmap); + return -1; +} + +static void cmp_index_allocation(ntfs_attr *na1, ntfs_attr *na2) +{ + struct cmp_ia cia1, cia2; + int bit, ret1, ret2; + u32 ib_size; + + if (setup_cmp_ia(na1, &cia1)) + return; + if (setup_cmp_ia(na2, &cia2)) + return; + /* + * FIXME: ia can be the same even if the bitmap sizes are different. + */ + if (cia1.bm_size != cia1.bm_size) + goto out; + + if (cmp_buffer(cia1.bitmap, cia2.bitmap, cia1.bm_size, na1)) + goto out; + + if (cmp_buffer((u8 *)cia1.ia, (u8 *)cia2.ia, 0x18, na1)) + goto out; + + ib_size = le32_to_cpu(cia1.ia->index.allocated_size) + 0x18; + + bit = 0; + while ((u8 *)cia1.tmp_ia < (u8 *)cia1.ia + na1->data_size) { + if (*cia1.byte & (1 << bit)) { + ret1 = ntfs_mst_post_read_fixup((NTFS_RECORD *) + cia1.tmp_ia, ib_size); + ret2 = ntfs_mst_post_read_fixup((NTFS_RECORD *) + cia2.tmp_ia, ib_size); + if (ret1 != ret2) { + print_differ(na1); + goto out; + } + + if (ret1 == -1) + continue; + + if (cmp_buffer(((u8 *)cia1.tmp_ia) + 0x18, + ((u8 *)cia2.tmp_ia) + 0x18, + le32_to_cpu(cia1.ia-> + index.index_length), na1)) + goto out; + } + + cia1.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia1.tmp_ia + ib_size); + cia2.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia2.tmp_ia + ib_size); + + bit++; + if (bit > 7) { + bit = 0; + cia1.byte++; + } + } +out: + free(cia1.ia); + free(cia2.ia); + free(cia1.bitmap); + free(cia2.bitmap); + return; +} + +static void cmp_attribute_data(ntfs_attr *na1, ntfs_attr *na2) +{ + s64 pos; + s64 count1 = 0, count2; + u8 buf1[NTFS_BUF_SIZE]; + u8 buf2[NTFS_BUF_SIZE]; + + for (pos = 0; pos <= na1->data_size; pos += count1) { + + count1 = ntfs_attr_pread(na1, pos, NTFS_BUF_SIZE, buf1); + count2 = ntfs_attr_pread(na2, pos, NTFS_BUF_SIZE, buf2); + + if (count1 != count2) { + print_na(na1); + printf("abrupt length: %lld != %lld ", + (long long)na1->data_size, + (long long)na2->data_size); + printf("(count: %lld != %lld)", + (long long)count1, (long long)count2); + puts(""); + return; + } + + if (count1 == -1) { + err_printf("%s read error: ", __FUNCTION__); + print_na(na1); + printf("len = %lld, pos = %lld\n", + (long long)na1->data_size, (long long)pos); + exit(1); + } + + if (count1 == 0) { + + if (pos + count1 == na1->data_size) + return; /* we are ready */ + + err_printf("%s read error before EOF: ", __FUNCTION__); + print_na(na1); + printf("%lld != %lld\n", (long long)pos + count1, + (long long)na1->data_size); + exit(1); + } + + if (cmp_buffer(buf1, buf2, count1, na1)) + return; + } + + err_printf("%s read overrun: ", __FUNCTION__); + print_na(na1); + err_printf("(len = %lld, pos = %lld, count = %lld)\n", + (long long)na1->data_size, (long long)pos, (long long)count1); + exit(1); +} + +static int cmp_attribute_header(ATTR_RECORD *a1, ATTR_RECORD *a2) +{ + u32 header_size = offsetof(ATTR_RECORD, resident_end); + + if (a1->non_resident != a2->non_resident) + return 1; + + if (a1->non_resident) { + /* + * FIXME: includes paddings which are not handled by ntfsinfo! + */ + header_size = le32_to_cpu(a1->length); + } + + return memcmp(a1, a2, header_size); +} + +static void cmp_attribute(ntfs_attr_search_ctx *ctx1, + ntfs_attr_search_ctx *ctx2) +{ + ATTR_RECORD *a1 = ctx1->attr; + ATTR_RECORD *a2 = ctx2->attr; + ntfs_attr *na1, *na2; + + if (cmp_attribute_header(a1, a2)) { + print_ctx(ctx1); + printf("header: DIFFER\n"); + } + + na1 = ntfs_attr_open(base_inode(ctx1), a1->type, GET_ATTR_NAME(a1)); + na2 = ntfs_attr_open(base_inode(ctx2), a2->type, GET_ATTR_NAME(a2)); + + if ((!na1 && na2) || (na1 && !na2)) { + print_ctx(ctx1); + printf("open: %s != %s\n", pret2str(na1), pret2str(na2)); + goto close_attribs; + } + + if (na1 == NULL) + goto close_attribs; + + if (na1->data_size != na2->data_size) { + print_na(na1); + printf("length: %lld != %lld\n", + (long long)na1->data_size, (long long)na2->data_size); + goto close_attribs; + } + + if (ntfs_inode_badclus_bad(inumber(ctx1->ntfs_ino), ctx1->attr) == 1) { + /* + * If difference exists then it's already reported at the + * attribute header since the mapping pairs must differ. + */ + goto close_attribs; + } + + if (na1->type == AT_INDEX_ALLOCATION) + cmp_index_allocation(na1, na2); + else + cmp_attribute_data(na1, na2); + +close_attribs: + ntfs_attr_close(na1); + ntfs_attr_close(na2); +} + +static void vprint_attribute(ATTR_TYPES atype, char *name) +{ + if (!opt.verbose) + return; + + printf("0x%x", atype); + if (name) + printf(":%s", name); + printf(" "); +} + +static void print_attributes(ntfs_inode *ni, + ATTR_TYPES atype1, + ATTR_TYPES atype2, + char *name1, + char *name2) +{ + if (!opt.verbose) + return; + + printf("Walking inode %llu attributes: ", + (unsigned long long)inumber(ni)); + vprint_attribute(atype1, name1); + vprint_attribute(atype2, name2); + printf("\n"); +} + +static int new_name(ntfs_attr_search_ctx *ctx, char *prev_name) +{ + int ret = 0; + char *name = get_attr_name_ctx(ctx); + + if (prev_name && name) { + if (strcmp(prev_name, name) != 0) + ret = 1; + } else if (prev_name || name) + ret = 1; + + free_name(&name); + return ret; + +} + +static int new_attribute(ntfs_attr_search_ctx *ctx, + ATTR_TYPES prev_atype, + char *prev_name) +{ + if (!prev_atype && !prev_name) + return 1; + + if (!ctx->attr->non_resident) + return 1; + + if (prev_atype != ctx->attr->type) + return 1; + + if (new_name(ctx, prev_name)) + return 1; + + if (opt.verbose) { + print_inode(base_inode(ctx)->mft_no); + print_attribute_ctx(ctx); + printf("record %llu lowest_vcn %lld: SKIPPED\n", + (unsigned long long)ctx->ntfs_ino->mft_no, + (long long)ctx->attr->lowest_vcn); + } + + return 0; +} + +static void set_prev(char **prev_name, ATTR_TYPES *prev_atype, + char *name, ATTR_TYPES atype) +{ + free_name(prev_name); + if (name) { + *prev_name = strdup(name); + if (!*prev_name) + perr_exit("strdup error"); + } + + *prev_atype = atype; +} + +static void set_cmp_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name) +{ + *atype = ctx->attr->type; + + free_name(name); + *name = get_attr_name_ctx(ctx); +} + +static int next_attr(ntfs_attr_search_ctx *ctx, ATTR_TYPES *atype, char **name, + int *err) +{ + int ret; + + ret = ntfs_attrs_walk(ctx); + *err = errno; + if (ret) { + *atype = AT_END; + free_name(name); + } else + set_cmp_attr(ctx, atype, name); + + return ret; +} + +static int cmp_attributes(ntfs_inode *ni1, ntfs_inode *ni2) +{ + int ret = -1; + int old_ret1, ret1 = 0, ret2 = 0; + int errno1 = 0, errno2 = 0; + char *prev_name = NULL, *name1 = NULL, *name2 = NULL; + ATTR_TYPES old_atype1, prev_atype = 0, atype1, atype2; + ntfs_attr_search_ctx *ctx1, *ctx2; + + if (!(ctx1 = attr_get_search_ctx(ni1))) + return -1; + if (!(ctx2 = attr_get_search_ctx(ni2))) + goto out; + + set_cmp_attr(ctx1, &atype1, &name1); + set_cmp_attr(ctx2, &atype2, &name2); + + while (1) { + + old_atype1 = atype1; + old_ret1 = ret1; + if (!ret1 && (le32_to_cpu(atype1) <= le32_to_cpu(atype2) || + ret2)) + ret1 = next_attr(ctx1, &atype1, &name1, &errno1); + if (!ret2 && (le32_to_cpu(old_atype1) >= le32_to_cpu(atype2) || + old_ret1)) + ret2 = next_attr(ctx2, &atype2, &name2, &errno2); + + print_attributes(ni1, atype1, atype2, name1, name2); + + if (ret1 && ret2) { + if (errno1 != errno2) { + print_inode_ni(ni1); + printf("attribute walk (errno): %d != %d\n", + errno1, errno2); + } + break; + } + + if (ret2 || le32_to_cpu(atype1) < le32_to_cpu(atype2)) { + if (new_attribute(ctx1, prev_atype, prev_name)) { + print_ctx(ctx1); + printf("presence: EXISTS != MISSING\n"); + set_prev(&prev_name, &prev_atype, name1, + atype1); + } + + } else if (ret1 || le32_to_cpu(atype1) > le32_to_cpu(atype2)) { + if (new_attribute(ctx2, prev_atype, prev_name)) { + print_ctx(ctx2); + printf("presence: MISSING != EXISTS \n"); + set_prev(&prev_name, &prev_atype, name2, atype2); + } + + } else /* atype1 == atype2 */ { + if (new_attribute(ctx1, prev_atype, prev_name)) { + cmp_attribute(ctx1, ctx2); + set_prev(&prev_name, &prev_atype, name1, atype1); + } + } + } + + free_name(&prev_name); + ret = 0; + ntfs_attr_put_search_ctx(ctx2); +out: + ntfs_attr_put_search_ctx(ctx1); + return ret; +} + +static int cmp_inodes(ntfs_volume *vol1, ntfs_volume *vol2) +{ + u64 inode; + int ret1, ret2; + ntfs_inode *ni1, *ni2; + struct progress_bar progress; + int pb_flags = 0; /* progress bar flags */ + u64 nr_mft_records, nr_mft_records2; + + if (opt.show_progress) + pb_flags |= NTFS_PROGBAR; + + nr_mft_records = get_nr_mft_records(vol1); + nr_mft_records2 = get_nr_mft_records(vol2); + + if (nr_mft_records != nr_mft_records2) { + + printf("Number of mft records: %lld != %lld\n", + (long long)nr_mft_records, (long long)nr_mft_records2); + + if (nr_mft_records > nr_mft_records2) + nr_mft_records = nr_mft_records2; + } + + progress_init(&progress, 0, nr_mft_records - 1, pb_flags); + progress_update(&progress, 0); + + for (inode = 0; inode < nr_mft_records; inode++) { + + ret1 = inode_open(vol1, (MFT_REF)inode, &ni1); + ret2 = inode_open(vol2, (MFT_REF)inode, &ni2); + + if (ret1 != ret2) { + print_inode(inode); + printf("open: %s != %s\n", + err2string(ret1), err2string(ret2)); + goto close_inodes; + } + + if (ret1 != NTFSCMP_OK) + goto close_inodes; + + if (cmp_attributes(ni1, ni2) != 0) { + inode_close(ni1); + inode_close(ni2); + return -1; + } +close_inodes: + if (inode_close(ni1) != 0) + return -1; + if (inode_close(ni2) != 0) + return -1; + + progress_update(&progress, inode); + } + return 0; +} + +static ntfs_volume *mount_volume(const char *volume) +{ + unsigned long mntflag; + ntfs_volume *vol = NULL; + + if (ntfs_check_if_mounted(volume, &mntflag)) { + perr_println("Failed to check '%s' mount state", volume); + printf("Probably /etc/mtab is missing. It's too risky to " + "continue. You might try\nan another Linux distro.\n"); + exit(1); + } + if (mntflag & NTFS_MF_MOUNTED) { + if (!(mntflag & NTFS_MF_READONLY)) + err_exit("Device '%s' is mounted read-write. " + "You must 'umount' it first.\n", volume); + } + + vol = ntfs_mount(volume, NTFS_MNT_RDONLY); + if (vol == NULL) { + + int err = errno; + + perr_println("Opening '%s' as NTFS failed", volume); + if (err == EINVAL) + printf(invalid_ntfs_msg, volume); + else if (err == EIO) + puts(corrupt_volume_msg); + else if (err == EPERM) + puts(hibernated_volume_msg); + exit(1); + } + + return vol; +} + +int main(int argc, char **argv) +{ + ntfs_volume *vol1; + ntfs_volume *vol2; + + printf("%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + + parse_options(argc, argv); + + utils_set_locale(); + + vol1 = mount_volume(opt.vol1); + vol2 = mount_volume(opt.vol2); + + if (cmp_inodes(vol1, vol2) != 0) + exit(1); + + ntfs_umount(vol1, FALSE); + ntfs_umount(vol2, FALSE); + + return (0); +} + diff --git a/ntfsprogs/ntfscp.8 b/ntfsprogs/ntfscp.8 new file mode 100755 index 0000000000000000000000000000000000000000..4f5838e6912d026fe2218e1290ad196c99408297 --- /dev/null +++ b/ntfsprogs/ntfscp.8 @@ -0,0 +1,115 @@ +.\" Copyright (c) 2004\-2007 Yura Pakhuchiy. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCP 8 "September 2007" "ntfs-3g 2015.3.14" +.SH NAME +ntfscp \- copy file to an NTFS volume. +.SH SYNOPSIS +\fBntfscp\fR [\fIoptions\fR] \fIdevice source_file destination\fR +.SH DESCRIPTION +\fBntfscp\fR will copy file to an NTFS volume. \fIdestination\fR can be either +file or directory. In case if \fIdestination\fR is directory specified by name +then \fIsource_file\fR is copied into this directory, in case if +\fIdestination\fR is directory and specified by inode number then unnamed data +attribute is created for this inode and \fIsource_file\fR is copied into it +(WARNING: it's unusual to have unnamed data streams in the directories, think +twice before specifying directory by inode number). +.SH OPTIONS +Below is a summary of all the options that +.B ntfscp +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-a\fR, \fB\-\-attribute\fR NUM +Write to this attribute. +.TP +\fB\-i\fR, \fB\-\-inode\fR +Treat +.I destination +as inode number. +.TP +\fB\-m\fR, \fB\-\-min-fragments\fR +Minimize fragmentation when allocating space to the attribute. This is +mostly useful when creating big files. +.TP +\fB\-N\fR, \fB\-\-attr\-name\fR NAME +Write to attribute with this name. +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Use this option to make a test run before doing the real copy operation. +Volume will be opened read\-only and no write will be done. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not working with a mounted +volume. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license +.BR ntfscp . +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.SH DATA STREAMS +All data on NTFS is stored in streams, which can have names. A file can have +more than one data streams, but exactly one must have no name. The size of a +file is the size of its unnamed data stream. Usually when you don't specify +stream name you are access to unnamed data stream. If you want access to named +data stream you need to add ":stream_name" to the filename. For example: by +opening "some.mp3:artist" you will open stream "artist" in "some.mp3". But +windows usually prevent you from accessing to named data streams, so you need +to use some program like FAR or utils from cygwin to access named data streams. +.SH EXAMPLES +Copy new_boot.ini from /home/user as boot.ini to the root of an /dev/hda1 NTFS +volume: +.RS +.sp +.B ntfscp /dev/hda1 /home/user/new_boot.ini boot.ini +.sp +.RE +Copy myfile to C:\\some\\path\\myfile:stream (assume that /dev/hda1 letter in +windows is C): +.RS +.sp +.B ntfscp \-N stream /dev/hda1 myfile /some/path +.sp +.RE +.SH BUGS +There are no known problems with \fBntfscp\fR. If you find a bug please send an +email describing the problem to the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +\fBntfscp\fR was written by Yura Pakhuchiy, with contributions from Anton +Altaparmakov and Hil Liao. +It was ported to ntfs-3g by Erik Larsson. +.SH DEDICATION +With love to Marina Sapego. +.SH AVAILABILITY +.B ntfscp +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscp.8.in b/ntfsprogs/ntfscp.8.in new file mode 100755 index 0000000000000000000000000000000000000000..bf5127baecf0119c38722ffb36e568e2b1f152d9 --- /dev/null +++ b/ntfsprogs/ntfscp.8.in @@ -0,0 +1,115 @@ +.\" Copyright (c) 2004\-2007 Yura Pakhuchiy. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSCP 8 "September 2007" "ntfs-3g @VERSION@" +.SH NAME +ntfscp \- copy file to an NTFS volume. +.SH SYNOPSIS +\fBntfscp\fR [\fIoptions\fR] \fIdevice source_file destination\fR +.SH DESCRIPTION +\fBntfscp\fR will copy file to an NTFS volume. \fIdestination\fR can be either +file or directory. In case if \fIdestination\fR is directory specified by name +then \fIsource_file\fR is copied into this directory, in case if +\fIdestination\fR is directory and specified by inode number then unnamed data +attribute is created for this inode and \fIsource_file\fR is copied into it +(WARNING: it's unusual to have unnamed data streams in the directories, think +twice before specifying directory by inode number). +.SH OPTIONS +Below is a summary of all the options that +.B ntfscp +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-a\fR, \fB\-\-attribute\fR NUM +Write to this attribute. +.TP +\fB\-i\fR, \fB\-\-inode\fR +Treat +.I destination +as inode number. +.TP +\fB\-m\fR, \fB\-\-min-fragments\fR +Minimize fragmentation when allocating space to the attribute. This is +mostly useful when creating big files. +.TP +\fB\-N\fR, \fB\-\-attr\-name\fR NAME +Write to attribute with this name. +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Use this option to make a test run before doing the real copy operation. +Volume will be opened read\-only and no write will be done. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not working with a mounted +volume. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license +.BR ntfscp . +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.SH DATA STREAMS +All data on NTFS is stored in streams, which can have names. A file can have +more than one data streams, but exactly one must have no name. The size of a +file is the size of its unnamed data stream. Usually when you don't specify +stream name you are access to unnamed data stream. If you want access to named +data stream you need to add ":stream_name" to the filename. For example: by +opening "some.mp3:artist" you will open stream "artist" in "some.mp3". But +windows usually prevent you from accessing to named data streams, so you need +to use some program like FAR or utils from cygwin to access named data streams. +.SH EXAMPLES +Copy new_boot.ini from /home/user as boot.ini to the root of an /dev/hda1 NTFS +volume: +.RS +.sp +.B ntfscp /dev/hda1 /home/user/new_boot.ini boot.ini +.sp +.RE +Copy myfile to C:\\some\\path\\myfile:stream (assume that /dev/hda1 letter in +windows is C): +.RS +.sp +.B ntfscp \-N stream /dev/hda1 myfile /some/path +.sp +.RE +.SH BUGS +There are no known problems with \fBntfscp\fR. If you find a bug please send an +email describing the problem to the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +\fBntfscp\fR was written by Yura Pakhuchiy, with contributions from Anton +Altaparmakov and Hil Liao. +It was ported to ntfs-3g by Erik Larsson. +.SH DEDICATION +With love to Marina Sapego. +.SH AVAILABILITY +.B ntfscp +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscp.c b/ntfsprogs/ntfscp.c new file mode 100755 index 0000000000000000000000000000000000000000..87a7da30055fba51f590823c5dfd5d92a87c12cd --- /dev/null +++ b/ntfsprogs/ntfscp.c @@ -0,0 +1,1133 @@ +/** + * ntfscp - Part of the Linux-NTFS project. + * + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2006 Hil Liao + * Copyright (c) 2014 Jean-Pierre Andre + * + * This utility will copy file to an NTFS volume. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#include <signal.h> +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_LIBGEN_H +#include <libgen.h> +#endif + +#include "types.h" +#include "attrib.h" +#include "utils.h" +#include "volume.h" +#include "dir.h" +#include "bitmap.h" +#include "debug.h" +/* #include "version.h" */ +#include "logging.h" +#include "misc.h" + +struct options { + char *device; /* Device/File to work with */ + char *src_file; /* Source file */ + char *dest_file; /* Destination file */ + char *attr_name; /* Write to attribute with this name. */ + int force; /* Override common sense */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int minfragments; /* Do minimal fragmentation */ + int noaction; /* Do not write to disk */ + ATTR_TYPES attribute; /* Write to this attribute. */ + int inode; /* Treat dest_file as inode number. */ +}; + +struct ALLOC_CONTEXT { + ntfs_volume *vol; + ntfs_attr *na; + runlist_element *rl; + unsigned char *buf; + s64 gathered_clusters; + s64 wanted_clusters; + s64 new_size; + s64 lcn; + int rl_allocated; + int rl_count; +} ; + +enum STEP { STEP_ERR, STEP_ZERO, STEP_ONE } ; + +static const char *EXEC_NAME = "ntfscp"; +static struct options opts; +static volatile sig_atomic_t caught_terminate = 0; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Copy file to an NTFS " + "volume.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2004-2007 Yura Pakhuchiy\n"); + ntfs_log_info("Copyright (c) 2005 Anton Altaparmakov\n"); + ntfs_log_info("Copyright (c) 2006 Hil Liao\n"); + ntfs_log_info("Copyright (c) 2014 Jean-Pierre Andre\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device src_file dest_file\n\n" + " -a, --attribute NUM Write to this attribute\n" + " -i, --inode Treat dest_file as inode number\n" + " -f, --force Use less caution\n" + " -h, --help Print this help\n" + " -m, --min_fragments Do minimal fragmentation\n" + " -N, --attr-name NAME Write to attribute with this name\n" + " -n, --no-action Do not write to disk\n" + " -q, --quiet Less output\n" + " -V, --version Version information\n" + " -v, --verbose More output\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-a:ifh?mN:no:qVv"; + static const struct option lopt[] = { + { "attribute", required_argument, NULL, 'a' }, + { "inode", no_argument, NULL, 'i' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "min-fragments", no_argument, NULL, 'm' }, + { "attr-name", required_argument, NULL, 'N' }, + { "no-action", no_argument, NULL, 'n' }, + { "quiet", no_argument, NULL, 'q' }, + { "version", no_argument, NULL, 'V' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + + char *s; + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + s64 attr; + + opts.device = NULL; + opts.src_file = NULL; + opts.dest_file = NULL; + opts.attr_name = NULL; + opts.inode = 0; + opts.attribute = AT_DATA; + + opterr = 0; /* We'll handle the errors, thank you. */ + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind - 1]; + } else if (!opts.src_file) { + opts.src_file = argv[optind - 1]; + } else if (!opts.dest_file) { + opts.dest_file = argv[optind - 1]; + } else { + ntfs_log_error("You must specify exactly two " + "files.\n"); + err++; + } + break; + case 'a': + if (opts.attribute != AT_DATA) { + ntfs_log_error("You can specify only one " + "attribute.\n"); + err++; + break; + } + + attr = strtol(optarg, &s, 0); + if (*s) { + ntfs_log_error("Couldn't parse attribute.\n"); + err++; + } else + opts.attribute = (ATTR_TYPES)cpu_to_le32(attr); + break; + case 'i': + opts.inode++; + break; + case 'f': + opts.force++; + break; + case 'h': + help++; + break; + case 'm': + opts.minfragments++; + break; + case 'N': + if (opts.attr_name) { + ntfs_log_error("You can specify only one " + "attribute name.\n"); + err++; + } else + opts.attr_name = argv[optind - 1]; + break; + case 'n': + opts.noaction++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'V': + ver++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case '?': + if (strncmp(argv[optind - 1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option(argv[optind - 1])) + err++; + break; + } + /* fall through */ + default: + ntfs_log_error("Unknown option '%s'.\n", + argv[optind - 1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (!opts.device) { + ntfs_log_error("You must specify a device.\n"); + err++; + } else if (!opts.src_file) { + ntfs_log_error("You must specify a source file.\n"); + err++; + } else if (!opts.dest_file) { + ntfs_log_error("You must specify a destination " + "file.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose " + "at the same time.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + /* tri-state 0 : done, 1 : error, -1 : proceed */ + return (err ? 1 : (help || ver ? 0 : -1)); +} + +/** + * signal_handler - Handle SIGINT and SIGTERM: abort write, sync and exit. + */ +static void signal_handler(int arg __attribute__((unused))) +{ + caught_terminate++; +} + +/* + * Search for the next '0' in a bitmap chunk + * + * Returns the position of next '0' + * or -1 if there are no more '0's + */ + +static int next_zero(struct ALLOC_CONTEXT *alctx, s32 bufpos, s32 count) +{ + s32 index; + unsigned int q,b; + + index = -1; + while ((index < 0) && (bufpos < count)) { + q = alctx->buf[bufpos >> 3]; + if (q == 255) + bufpos = (bufpos | 7) + 1; + else { + b = bufpos & 7; + while ((b < 8) + && ((1 << b) & q)) + b++; + if (b < 8) { + index = (bufpos & -8) | b; + } else { + bufpos = (bufpos | 7) + 1; + } + } + } + return (index); +} + +/* + * Search for the next '1' in a bitmap chunk + * + * Returns the position of next '1' + * or -1 if there are no more '1's + */ + +static int next_one(struct ALLOC_CONTEXT *alctx, s32 bufpos, s32 count) +{ + s32 index; + unsigned int q,b; + + index = -1; + while ((index < 0) && (bufpos < count)) { + q = alctx->buf[bufpos >> 3]; + if (q == 0) + bufpos = (bufpos | 7) + 1; + else { + b = bufpos & 7; + while ((b < 8) + && !((1 << b) & q)) + b++; + if (b < 8) { + index = (bufpos & -8) | b; + } else { + bufpos = (bufpos | 7) + 1; + } + } + } + return (index); +} + +/* + * Allocate a bigger runlist when needed + * + * The allocation is done by multiple of 4096 entries to avoid + * frequent reallocations. + * + * Returns 0 if successful + * -1 otherwise, with errno set accordingly + */ + +static int run_alloc(struct ALLOC_CONTEXT *alctx, s32 count) +{ + runlist_element *prl; + int err; + + err = 0; + if (count > alctx->rl_allocated) { + prl = (runlist_element*)ntfs_malloc( + (alctx->rl_allocated + 4096)*sizeof(runlist_element)); + if (prl) { + if (alctx->rl) { + memcpy(prl, alctx->rl, alctx->rl_allocated + *sizeof(runlist_element)); + free(alctx->rl); + } + alctx->rl = prl; + alctx->rl_allocated += 4096; + } else + err = -1; + } + return (err); +} + +/* + * Merge a new run into the current optimal runlist + * + * The new run is inserted only if it leads to improving the runlist. + * Runs in the current list are dropped when inserting the new one + * make them unneeded. + * The current runlist is sorted by run sizes, and there is no + * terminator. + * + * Returns 0 if successful + * -1 otherwise, with errno set accordingly + */ + +static int merge_run(struct ALLOC_CONTEXT *alctx, s64 lcn, s32 count) +{ + s64 excess; + BOOL replace; + int k; + int drop; + int err; + + err = 0; + if (alctx->rl_count) { + excess = alctx->gathered_clusters + count + - alctx->wanted_clusters; + if (alctx->rl_count > 1) + /* replace if we can reduce the number of runs */ + replace = excess > (alctx->rl[0].length + + alctx->rl[1].length); + else + /* replace if we can shorten a single run */ + replace = (excess > alctx->rl[0].length) + && (count < alctx->rl[0].length); + } else + replace = FALSE; + if (replace) { + /* Using this run, we can now drop smaller runs */ + drop = 0; + excess = alctx->gathered_clusters + count + - alctx->wanted_clusters; + /* Compute how many clusters we can drop */ + while ((drop < alctx->rl_count) + && (alctx->rl[drop].length <= excess)) { + excess -= alctx->rl[drop].length; + drop++; + } + k = 0; + while (((k + drop) < alctx->rl_count) + && (alctx->rl[k + drop].length < count)) { + alctx->rl[k] = alctx->rl[k + drop]; + k++; + } + alctx->rl[k].length = count; + alctx->rl[k].lcn = lcn; + if (drop > 1) { + while ((k + drop) < alctx->rl_count) { + alctx->rl[k + 1] = alctx->rl[k + drop]; + k++; + } + } + alctx->rl_count -= (drop - 1); + alctx->gathered_clusters = alctx->wanted_clusters + excess; + } else { + if (alctx->gathered_clusters < alctx->wanted_clusters) { + /* We had not gathered enough clusters */ + if (!run_alloc(alctx, alctx->rl_count + 1)) { + k = alctx->rl_count - 1; + while ((k >= 0) + && (alctx->rl[k].length > count)) { + alctx->rl[k+1] = alctx->rl[k]; + k--; + } + alctx->rl[k+1].length = count; + alctx->rl[k+1].lcn = lcn; + alctx->rl_count++; + alctx->gathered_clusters += count; + } + } + } + return (err); +} + +/* + * Examine a buffer from the global bitmap + * in order to locate free runs of clusters + * + * Returns STEP_ZERO or STEP_ONE depending on whether the last + * bit examined was in a search for '0' or '1'. This must be + * put as argument to next examination. + * Returns STEP_ERR if there was an error. + */ + +static enum STEP examine_buf(struct ALLOC_CONTEXT *alctx, s64 pos, s64 br, + enum STEP step) +{ + s32 count; + s64 offbuf; /* first bit available in buf */ + s32 bufpos; /* bit index in buf */ + s32 index; + + bufpos = pos & ((alctx->vol->cluster_size << 3) - 1); + offbuf = pos - bufpos; + while (bufpos < (br << 3)) { + if (step == STEP_ZERO) { + /* find first zero */ + index = next_zero(alctx, bufpos, br << 3); + if (index >= 0) { + alctx->lcn = offbuf + index; + step = STEP_ONE; + bufpos = index; + } else { + bufpos = br << 3; + } + } else { + /* find first one */ + index = next_one(alctx, bufpos, br << 3); + if (index >= 0) { + count = offbuf + index - alctx->lcn; + step = STEP_ZERO; + bufpos = index; + if (merge_run(alctx, alctx->lcn, count)) { + step = STEP_ERR; + bufpos = br << 3; + } + } else { + bufpos = br << 3; + } + } + } + return (step); +} + +/* + * Sort the final runlist by lcn's and insert a terminator + * + * Returns 0 if successful + * -1 otherwise, with errno set accordingly + */ + +static int sort_runlist(struct ALLOC_CONTEXT *alctx) +{ + LCN lcn; + VCN vcn; + s64 length; + BOOL sorted; + int err; + int k; + + err = 0; + /* This sorting can be much improved... */ + do { + sorted = TRUE; + for (k=0; (k+1)<alctx->rl_count; k++) { + if (alctx->rl[k+1].lcn < alctx->rl[k].lcn) { + length = alctx->rl[k].length; + lcn = alctx->rl[k].lcn; + alctx->rl[k] = alctx->rl[k+1]; + alctx->rl[k+1].length = length; + alctx->rl[k+1].lcn = lcn; + sorted = FALSE; + } + } + } while (!sorted); + /* compute the vcns */ + vcn = 0; + for (k=0; k<alctx->rl_count; k++) { + alctx->rl[k].vcn = vcn; + vcn += alctx->rl[k].length; + } + /* Shorten the last run if we got too much */ + if (vcn > alctx->wanted_clusters) { + k = alctx->rl_count - 1; + alctx->rl[k].length -= vcn - alctx->wanted_clusters; + vcn = alctx->wanted_clusters; + } + /* Append terminator */ + if (run_alloc(alctx, alctx->rl_count + 1)) + err = -1; + else { + k = alctx->rl_count++; + alctx->rl[k].vcn = vcn; + alctx->rl[k].length = 0; + alctx->rl[k].lcn = LCN_ENOENT; + } + return (err); +} + +/* + * Update the sizes of an attribute + * + * Returns 0 if successful + * -1 otherwise, with errno set accordingly + */ + +static int set_sizes(struct ALLOC_CONTEXT *alctx, ntfs_attr_search_ctx *ctx) +{ + ntfs_attr *na; + ntfs_inode *ni; + ATTR_RECORD *attr; + + na = alctx->na; + /* Compute the sizes */ + na->data_size = alctx->new_size; + na->initialized_size = 0; + na->allocated_size = alctx->wanted_clusters + << alctx->vol->cluster_size_bits; + /* Feed the sizes into the attribute */ + attr = ctx->attr; + attr->non_resident = 1; + attr->data_size = cpu_to_le64(na->data_size); + attr->initialized_size = cpu_to_le64(na->initialized_size); + attr->allocated_size = cpu_to_le64(na->allocated_size); + if (na->data_flags & ATTR_IS_SPARSE) + attr->compressed_size = cpu_to_le64(na->compressed_size); + /* Copy the unnamed data attribute sizes to inode */ + if ((opts.attribute == AT_DATA) && !na->name_len) { + ni = na->ni; + ni->data_size = na->data_size; + if (na->data_flags & ATTR_IS_SPARSE) { + ni->allocated_size = na->compressed_size; + ni->flags |= FILE_ATTR_SPARSE_FILE; + } else + ni->allocated_size = na->allocated_size; + } + return (0); +} + +/* + * Assign a runlist to an attribute and store + * + * Returns 0 if successful + * -1 otherwise, with errno set accordingly + */ + +static int assign_runlist(struct ALLOC_CONTEXT *alctx) +{ + ntfs_attr *na; + ntfs_attr_search_ctx *ctx; + int k; + int err; + + err = 0; + na = alctx->na; + if (na->rl) + free(na->rl); + na->rl = alctx->rl; + /* Allocate the clusters */ + for (k=0; ((k + 1) < alctx->rl_count) && !err; k++) { + if (ntfs_bitmap_set_run(alctx->vol->lcnbmp_na, + alctx->rl[k].lcn, alctx->rl[k].length)) { + err = -1; + } + } + na->allocated_size = alctx->wanted_clusters + << alctx->vol->cluster_size_bits; + NAttrSetNonResident(na); + NAttrSetFullyMapped(na); + if (err || ntfs_attr_update_mapping_pairs(na, 0)) { + err = -1; + } else { + ctx = ntfs_attr_get_search_ctx(alctx->na->ni, NULL); + if (ctx) { + if (ntfs_attr_lookup(opts.attribute, na->name, + na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + err = -1; + } else { + if (set_sizes(alctx, ctx)) + err = -1; + } + } else + err = -1; + ntfs_attr_put_search_ctx(ctx); + } + return (err); +} + +/* + * Find the runs which minimize fragmentation + * + * Only the first and second data zones are examined, the MFT zone + * is preserved. + * + * Returns 0 if successful + * -1 otherwise, with errno set accordingly + */ + +static int find_best_runs(struct ALLOC_CONTEXT *alctx) +{ + ntfs_volume *vol; + s64 pos; /* bit index in bitmap */ + s64 br; /* byte count in buf */ + int err; + enum STEP step; + + err = 0; + vol = alctx->vol; + /* examine the first data zone */ + pos = vol->mft_zone_end; + br = vol->cluster_size; + step = STEP_ZERO; + while ((step != STEP_ERR) + && (br == vol->cluster_size) + && (pos < vol->nr_clusters)) { + br = ntfs_attr_pread(vol->lcnbmp_na, + (pos >> 3) & -vol->cluster_size, + vol->cluster_size, alctx->buf); + if (br > 0) { + step = examine_buf(alctx, pos, br, step); + pos = (pos | ((vol->cluster_size << 3) - 1)) + 1; + } + } + /* examine the second data zone */ + pos = 0; + br = vol->cluster_size; + step = STEP_ZERO; + while ((step != STEP_ERR) + && (br == vol->cluster_size) + && (pos < vol->mft_zone_start)) { + br = ntfs_attr_pread(vol->lcnbmp_na, + (pos >> 3) & -vol->cluster_size, + vol->cluster_size, alctx->buf); + if (br > 0) { + step = examine_buf(alctx, pos, br, step); + pos = (pos | ((vol->cluster_size << 3) - 1)) + 1; + } + } + if (alctx->gathered_clusters < alctx->wanted_clusters) { + errno = ENOSPC; + ntfs_log_error("Error : not enough space on device\n"); + err = -1; + } else { + if ((step == STEP_ERR) || sort_runlist(alctx)) + err = -1; + } + return (err); +} + +/* + * Preallocate clusters with minimal fragmentation + * + * Returns 0 if successful + * -1 otherwise, with errno set accordingly + */ + +static int preallocate(ntfs_attr *na, s64 new_size) +{ + struct ALLOC_CONTEXT *alctx; + ntfs_volume *vol; + int err; + + err = 0; + vol = na->ni->vol; + alctx = (struct ALLOC_CONTEXT*)ntfs_malloc(sizeof(struct ALLOC_CONTEXT)); + if (alctx) { + alctx->buf = (unsigned char*)ntfs_malloc(vol->cluster_size); + if (alctx->buf) { + alctx->na = na; + alctx->vol = vol; + alctx->rl_count = 0; + alctx->rl_allocated = 0; + alctx->rl = (runlist_element*)NULL; + alctx->new_size = new_size; + alctx->wanted_clusters = (new_size + + vol->cluster_size - 1) + >> vol->cluster_size_bits; + alctx->gathered_clusters = 0; + if (find_best_runs(alctx)) + err = -1; + if (!err && !opts.noaction) { + if (assign_runlist(alctx)) + err = -1; + } else + free(alctx->rl); + free(alctx->buf); + } else + err = -1; + free(alctx); + } else + err = -1; + return (err); +} + +/** + * Create a regular file under the given directory inode + * + * It is a wrapper function to ntfs_create(...) + * + * Return: the created file inode + */ +static ntfs_inode *ntfs_new_file(ntfs_inode *dir_ni, + const char *filename) +{ + ntfschar *ufilename; + /* inode to the file that is being created */ + ntfs_inode *ni; + int ufilename_len; + + /* ntfs_mbstoucs(...) will allocate memory for ufilename if it's NULL */ + ufilename = NULL; + ufilename_len = ntfs_mbstoucs(filename, &ufilename); + if (ufilename_len == -1) { + ntfs_log_perror("ERROR: Failed to convert '%s' to unicode", + filename); + return NULL; + } + ni = ntfs_create(dir_ni, 0, ufilename, ufilename_len, S_IFREG); + free(ufilename); + return ni; +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + FILE *in; + ntfs_volume *vol; + ntfs_inode *out; + ntfs_attr *na; + int flags = 0; + int res; + int result = 1; + s64 new_size; + u64 offset; + char *buf; + s64 br, bw; + ntfschar *attr_name; + int attr_name_len = 0; + + ntfs_log_set_handler(ntfs_log_handler_stderr); + + res = parse_options(argc, argv); + if (res >= 0) + return (res); + + utils_set_locale(); + + /* Set SIGINT handler. */ + if (signal(SIGINT, signal_handler) == SIG_ERR) { + ntfs_log_perror("Failed to set SIGINT handler"); + return 1; + } + /* Set SIGTERM handler. */ + if (signal(SIGTERM, signal_handler) == SIG_ERR) { + ntfs_log_perror("Failed to set SIGTERM handler"); + return 1; + } + + if (opts.noaction) + flags = NTFS_MNT_RDONLY; + if (opts.force) + flags |= NTFS_MNT_RECOVER; + + vol = utils_mount_volume(opts.device, flags); + if (!vol) { + ntfs_log_perror("ERROR: couldn't mount volume"); + return 1; + } + + if ((vol->flags & VOLUME_IS_DIRTY) && !opts.force) + goto umount; + + NVolSetCompression(vol); /* allow compression */ + if (ntfs_volume_get_free_space(vol)) { + ntfs_log_perror("ERROR: couldn't get free space"); + goto umount; + } + + { + struct stat fst; + if (stat(opts.src_file, &fst) == -1) { + ntfs_log_perror("ERROR: Couldn't stat source file"); + goto umount; + } + new_size = fst.st_size; + } + ntfs_log_verbose("New file size: %lld\n", (long long)new_size); + + in = fopen(opts.src_file, "r"); + if (!in) { + ntfs_log_perror("ERROR: Couldn't open source file"); + goto umount; + } + + if (opts.inode) { + s64 inode_num; + char *s; + + inode_num = strtoll(opts.dest_file, &s, 0); + if (*s) { + ntfs_log_error("ERROR: Couldn't parse inode number.\n"); + goto close_src; + } + out = ntfs_inode_open(vol, inode_num); + } else + out = ntfs_pathname_to_inode(vol, NULL, opts.dest_file); + if (!out) { + /* Copy the file if the dest_file's parent dir can be opened. */ + char *parent_dirname; + char *filename; + ntfs_inode *dir_ni; + ntfs_inode *ni; + char *dirname_last_whack; + + filename = basename(opts.dest_file); + parent_dirname = strdup(opts.dest_file); + if (!parent_dirname) { + ntfs_log_perror("strdup() failed"); + goto close_src; + } + dirname_last_whack = strrchr(parent_dirname, '/'); + if (dirname_last_whack) { + if (dirname_last_whack == parent_dirname) + dirname_last_whack[1] = 0; + else + *dirname_last_whack = 0; + dir_ni = ntfs_pathname_to_inode(vol, NULL, + parent_dirname); + } else { + ntfs_log_verbose("Target path does not contain '/'. " + "Using root directory as parent.\n"); + dir_ni = ntfs_inode_open(vol, FILE_root); + } + if (dir_ni) { + if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + /* Remove the last '/' for estetic reasons. */ + dirname_last_whack[0] = 0; + ntfs_log_error("The file '%s' already exists " + "and is not a directory. " + "Aborting.\n", parent_dirname); + free(parent_dirname); + ntfs_inode_close(dir_ni); + goto close_src; + } + ntfs_log_verbose("Creating a new file '%s' under '%s'" + "\n", filename, parent_dirname); + ni = ntfs_new_file(dir_ni, filename); + ntfs_inode_close(dir_ni); + if (!ni) { + ntfs_log_perror("Failed to create '%s' under " + "'%s'", filename, + parent_dirname); + free(parent_dirname); + goto close_src; + } + out = ni; + } else { + ntfs_log_perror("ERROR: Couldn't open '%s'", + parent_dirname); + free(parent_dirname); + goto close_src; + } + free(parent_dirname); + } + /* The destination is a directory. */ + if ((out->mrec->flags & MFT_RECORD_IS_DIRECTORY) && !opts.inode) { + char *filename; + char *overwrite_filename; + int overwrite_filename_len; + ntfs_inode *ni; + ntfs_inode *dir_ni; + int filename_len; + int dest_dirname_len; + + filename = basename(opts.src_file); + dir_ni = out; + filename_len = strlen(filename); + dest_dirname_len = strlen(opts.dest_file); + overwrite_filename_len = filename_len+dest_dirname_len + 2; + overwrite_filename = malloc(overwrite_filename_len); + if (!overwrite_filename) { + ntfs_log_perror("ERROR: Failed to allocate %i bytes " + "memory for the overwrite filename", + overwrite_filename_len); + ntfs_inode_close(out); + goto close_src; + } + strcpy(overwrite_filename, opts.dest_file); + if (opts.dest_file[dest_dirname_len - 1] != '/') { + strcat(overwrite_filename, "/"); + } + strcat(overwrite_filename, filename); + ni = ntfs_pathname_to_inode(vol, dir_ni, overwrite_filename); + /* Does a file with the same name exist in the dest dir? */ + if (ni) { + ntfs_log_verbose("Destination path has a file with " + "the same name\nOverwriting the file " + "'%s'\n", overwrite_filename); + ntfs_inode_close(out); + out = ni; + } else { + ntfs_log_verbose("Creating a new file '%s' under " + "'%s'\n", filename, opts.dest_file); + ni = ntfs_new_file(dir_ni, filename); + ntfs_inode_close(dir_ni); + if (!ni) { + ntfs_log_perror("ERROR: Failed to create the " + "destination file under '%s'", + opts.dest_file); + free(overwrite_filename); + goto close_src; + } + out = ni; + } + free(overwrite_filename); + } + + attr_name = ntfs_str2ucs(opts.attr_name, &attr_name_len); + if (!attr_name) { + ntfs_log_perror("ERROR: Failed to parse attribute name '%s'", + opts.attr_name); + goto close_dst; + } + + na = ntfs_attr_open(out, opts.attribute, attr_name, attr_name_len); + if (!na) { + if (errno != ENOENT) { + ntfs_log_perror("ERROR: Couldn't open attribute"); + goto close_dst; + } + /* Requested attribute isn't present, add it. */ + if (ntfs_attr_add(out, opts.attribute, attr_name, + attr_name_len, NULL, 0)) { + ntfs_log_perror("ERROR: Couldn't add attribute"); + goto close_dst; + } + na = ntfs_attr_open(out, opts.attribute, attr_name, + attr_name_len); + if (!na) { + ntfs_log_perror("ERROR: Couldn't open just added " + "attribute"); + goto close_dst; + } + } + + ntfs_log_verbose("Old file size: %lld\n", (long long)na->data_size); + if (opts.minfragments && NAttrCompressed(na)) { + ntfs_log_info("Warning : Cannot avoid fragmentation" + " of a compressed attribute\n"); + opts.minfragments = 0; + } + if (na->data_size && opts.minfragments) { + if (ntfs_attr_truncate(na, 0)) { + ntfs_log_perror( + "ERROR: Couldn't truncate existing attribute"); + goto close_attr; + } + } + if (na->data_size != new_size) { + if (opts.minfragments) { + /* + * Do a standard truncate() to check whether the + * attribute has to be made non-resident. + * If still resident, preallocation is not needed. + */ + if (ntfs_attr_truncate(na, new_size)) { + ntfs_log_perror( + "ERROR: Couldn't resize attribute"); + goto close_attr; + } + if (NAttrNonResident(na) + && preallocate(na, new_size)) { + ntfs_log_perror( + "ERROR: Couldn't preallocate attribute"); + goto close_attr; + } + } else { + if (ntfs_attr_truncate_solid(na, new_size)) { + ntfs_log_perror( + "ERROR: Couldn't resize attribute"); + goto close_attr; + } + } + } + + buf = malloc(NTFS_BUF_SIZE); + if (!buf) { + ntfs_log_perror("ERROR: malloc failed"); + goto close_attr; + } + + ntfs_log_verbose("Starting write.\n"); + offset = 0; + while (!feof(in)) { + if (caught_terminate) { + ntfs_log_error("SIGTERM or SIGINT received. " + "Aborting write.\n"); + break; + } + br = fread(buf, 1, NTFS_BUF_SIZE, in); + if (!br) { + if (!feof(in)) ntfs_log_perror("ERROR: fread failed"); + break; + } + bw = ntfs_attr_pwrite(na, offset, br, buf); + if (bw != br) { + ntfs_log_perror("ERROR: ntfs_attr_pwrite failed"); + break; + } + offset += bw; + } + if ((na->data_flags & ATTR_COMPRESSION_MASK) + && ntfs_attr_pclose(na)) + ntfs_log_perror("ERROR: ntfs_attr_pclose failed"); + ntfs_log_verbose("Syncing.\n"); + result = 0; + free(buf); +close_attr: + ntfs_attr_close(na); +close_dst: + while (ntfs_inode_close(out) && !opts.noaction) { + if (errno != EBUSY) { + ntfs_log_error("Sync failed. Run chkdsk.\n"); + break; + } + ntfs_log_error("Device busy. Will retry sync in 3 seconds.\n"); + sleep(3); + } +close_src: + fclose(in); +umount: + ntfs_umount(vol, FALSE); + ntfs_log_verbose("Done.\n"); + return result; +} diff --git a/ntfsprogs/ntfsdecrypt.8 b/ntfsprogs/ntfsdecrypt.8 new file mode 100755 index 0000000000000000000000000000000000000000..3320d3d13ae0518151b50d72beaf282fffe636ee --- /dev/null +++ b/ntfsprogs/ntfsdecrypt.8 @@ -0,0 +1,129 @@ +.\" Copyright (c) 2014 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSDECRYPT 8 "June 2014" "ntfs-3g 2015.3.14" +.SH NAME +ntfsdecrypt \- decrypt or update NTFS files encrypted according to EFS +.SH SYNOPSIS +\fBntfsdecrypt\fR [\fIoptions\fR] -k \fIkey.pfx \fIdevice file\fR +.SH DESCRIPTION +.B ntfsdecrypt +decrypts a file from an unmounted device and print the decrypted data +on the standard output. +It can also update an encrypted file with the encryption key unchanged. +.PP +The NTFS file encryption (known as EFS) uses a two-level encryption : +first, the file contents is encrypted with a random symmetric key, then +this symmetric key is encrypted with the public keys of each of the users +allowed to decrypt the file (RSA public key encryptions). +.P +Three symmetric encryption modes are currently implemented in ntfsdecrypt : +DESX (a DES variant), 3DES (triple DES) and AES_256 (an AES variant). +.P +All the encrypted symmetric keys are stored along with the file in a +special extended attribute named "$LOGGED_UTILITY_STREAM". +Usually, at least two users are allowed to read the file : its owner and +the recovery manager who is able to decrypt all the files in a company. +When backing up an encrypted file, it is important to also backup the +corresponding $LOGGED_UTILITY_STREAM, otherwise the file cannot be +decrypted, even by the recovery manager. Also note that encrypted files +are slightly bigger than apparent, and the option "efs_raw" has +to be used when backing up encrypted files with ntfs-3g. +.P +When ntfsdecrypt is used to update a file, the keys and the +$LOGGED_UTILITY_STREAM are kept unchanged, so a single key file has to +be designated. +.P +Note : the EFS encryption is only available in professional versions +of Windows; +.SH OPTIONS +Below is a summary of all the options that +.B ntfsdecrypt +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-i\fR, \fB\-\-inode\fR NUM +Display or update the contents of a file designated through its inode number +instead of its name. +.TP +\fB\-e\fR, \fB\-\-encrypt\fR +Update an existing encrypted file and get the new contents from the +standard input. The full public and private key file has to be designated, +as the symmetric key is kept unchanged, so the private key is needed to +extract it. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not using a mounted volume. +Use this option with caution. +.TP +\fB\-k\fR, \fB\-\-keyfile\-name\fR key.pfx +Define the file which contains the public and private keys in PKCS#12 format. +This file obviously contains the keys of one of the users allowed to decrypt +or update the file. It has to be extracted from Windows in PKCS#12 format +(its usual suffix is .p12 or .pfx), and it is protected by a passphrase +which has to be typed in for the keys to be extracted. This can be the +key file of any user allowed to read the file, including the one of the +recovery manager. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license of +.BR ntfsdecrypt . +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.SH EXAMPLES +Display the contents of the file hamlet.doc in the directory Documents of +the root of the NTFS file system on the device /dev/sda1 +.RS +.sp +.B ntfsdecrypt -k foo.key /dev/sda1 Documents/hamlet.doc +.sp +.RE +Update the file hamlet.doc +.RS +.sp +.B ntfsdecrypt -k foo.key /dev/sda1 Documents/hamlet.doc < new.doc +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfsdecrypt . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfsdecrypt +was written by Yuval Fledel, Anton Altaparmakov and Yura Pakhuchiy. +It was ported to ntfs-3g by Erik Larsson and upgraded by Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsdecrypt +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +Read \fBntfs-3g\fR(8) for details on option efs_raw, +.RE +.BR ntfscat (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsdecrypt.8.in b/ntfsprogs/ntfsdecrypt.8.in new file mode 100755 index 0000000000000000000000000000000000000000..fd156b071845db08232416e7304732a280fa057c --- /dev/null +++ b/ntfsprogs/ntfsdecrypt.8.in @@ -0,0 +1,129 @@ +.\" Copyright (c) 2014 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSDECRYPT 8 "June 2014" "ntfs-3g @VERSION@" +.SH NAME +ntfsdecrypt \- decrypt or update NTFS files encrypted according to EFS +.SH SYNOPSIS +\fBntfsdecrypt\fR [\fIoptions\fR] -k \fIkey.pfx \fIdevice file\fR +.SH DESCRIPTION +.B ntfsdecrypt +decrypts a file from an unmounted device and print the decrypted data +on the standard output. +It can also update an encrypted file with the encryption key unchanged. +.PP +The NTFS file encryption (known as EFS) uses a two-level encryption : +first, the file contents is encrypted with a random symmetric key, then +this symmetric key is encrypted with the public keys of each of the users +allowed to decrypt the file (RSA public key encryptions). +.P +Three symmetric encryption modes are currently implemented in ntfsdecrypt : +DESX (a DES variant), 3DES (triple DES) and AES_256 (an AES variant). +.P +All the encrypted symmetric keys are stored along with the file in a +special extended attribute named "$LOGGED_UTILITY_STREAM". +Usually, at least two users are allowed to read the file : its owner and +the recovery manager who is able to decrypt all the files in a company. +When backing up an encrypted file, it is important to also backup the +corresponding $LOGGED_UTILITY_STREAM, otherwise the file cannot be +decrypted, even by the recovery manager. Also note that encrypted files +are slightly bigger than apparent, and the option "efs_raw" has +to be used when backing up encrypted files with ntfs-3g. +.P +When ntfsdecrypt is used to update a file, the keys and the +$LOGGED_UTILITY_STREAM are kept unchanged, so a single key file has to +be designated. +.P +Note : the EFS encryption is only available in professional versions +of Windows; +.SH OPTIONS +Below is a summary of all the options that +.B ntfsdecrypt +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-i\fR, \fB\-\-inode\fR NUM +Display or update the contents of a file designated through its inode number +instead of its name. +.TP +\fB\-e\fR, \fB\-\-encrypt\fR +Update an existing encrypted file and get the new contents from the +standard input. The full public and private key file has to be designated, +as the symmetric key is kept unchanged, so the private key is needed to +extract it. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not using a mounted volume. +Use this option with caution. +.TP +\fB\-k\fR, \fB\-\-keyfile\-name\fR key.pfx +Define the file which contains the public and private keys in PKCS#12 format. +This file obviously contains the keys of one of the users allowed to decrypt +or update the file. It has to be extracted from Windows in PKCS#12 format +(its usual suffix is .p12 or .pfx), and it is protected by a passphrase +which has to be typed in for the keys to be extracted. This can be the +key file of any user allowed to read the file, including the one of the +recovery manager. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license of +.BR ntfsdecrypt . +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.SH EXAMPLES +Display the contents of the file hamlet.doc in the directory Documents of +the root of the NTFS file system on the device /dev/sda1 +.RS +.sp +.B ntfsdecrypt -k foo.key /dev/sda1 Documents/hamlet.doc +.sp +.RE +Update the file hamlet.doc +.RS +.sp +.B ntfsdecrypt -k foo.key /dev/sda1 Documents/hamlet.doc < new.doc +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfsdecrypt . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfsdecrypt +was written by Yuval Fledel, Anton Altaparmakov and Yura Pakhuchiy. +It was ported to ntfs-3g by Erik Larsson and upgraded by Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsdecrypt +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +Read \fBntfs-3g\fR(8) for details on option efs_raw, +.RE +.BR ntfscat (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsdecrypt.c b/ntfsprogs/ntfsdecrypt.c new file mode 100755 index 0000000000000000000000000000000000000000..ab58cbd989c8a75d0edce972cbf5d8005deeb1ae --- /dev/null +++ b/ntfsprogs/ntfsdecrypt.c @@ -0,0 +1,1623 @@ +/** + * ntfsdecrypt - Decrypt ntfs encrypted files. Part of the Linux-NTFS project. + * + * Copyright (c) 2005 Yuval Fledel + * Copyright (c) 2005-2007 Anton Altaparmakov + * Copyright (c) 2007 Yura Pakhuchiy + * Copyright (c) 2014 Jean-Pierre Andre + * + * This utility will decrypt files and print the decrypted data on the standard + * output. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <gcrypt.h> +#include <gnutls/pkcs12.h> + +#include "types.h" +#include "attrib.h" +#include "utils.h" +#include "volume.h" +#include "debug.h" +#include "dir.h" +#include "layout.h" +/* #include "version.h" */ +#include "misc.h" + +typedef gcry_sexp_t ntfs_rsa_private_key; + +#define NTFS_SHA1_THUMBPRINT_SIZE 0x14 + +#define NTFS_CRED_TYPE_CERT_THUMBPRINT const_cpu_to_le32(3) + +#define NTFS_EFS_CERT_PURPOSE_OID_DDF "1.3.6.1.4.1.311.10.3.4" /* decryption */ +#define NTFS_EFS_CERT_PURPOSE_OID_DRF "1.3.6.1.4.1.311.10.3.4.1" /* recovery */ + +typedef enum { + DF_TYPE_UNKNOWN, + DF_TYPE_DDF, /* decryption */ + DF_TYPE_DRF, /* recovery */ +} NTFS_DF_TYPES; + +/** + * enum NTFS_CRYPTO_ALGORITHMS - List of crypto algorithms used by EFS (32 bit) + * + * To choose which one is used in Windows, create or set the REG_DWORD registry + * key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\EFS\ + * AlgorithmID to the value of your chosen crypto algorithm, e.g. to use DesX, + * set AlgorithmID to 0x6604. + * + * Note that the Windows versions I have tried so far (all are high crypto + * enabled) ignore the AlgorithmID value if it is not one of CALG_3DES, + * CALG_DESX, or CALG_AES_256, i.e. you cannot select CALG_DES at all using + * this registry key. It would be interesting to check out encryption on one + * of the "crippled" crypto Windows versions... + */ +typedef enum { + CALG_DES = const_cpu_to_le32(0x6601), + /* If not one of the below three, fall back to standard Des. */ + CALG_3DES = const_cpu_to_le32(0x6603), + CALG_DESX = const_cpu_to_le32(0x6604), + CALG_AES_256 = const_cpu_to_le32(0x6610), +} NTFS_CRYPTO_ALGORITHMS; + +typedef struct { + u64 in_whitening, out_whitening; + u8 des_key[8]; +} ntfs_desx_ctx; + +/** + * struct ntfs_fek - Decrypted, in-memory file encryption key. + */ + +typedef struct { + gcry_cipher_hd_t gcry_cipher_hd; + le32 alg_id; + u8 *key_data; + gcry_cipher_hd_t *des_gcry_cipher_hd_ptr; + ntfs_desx_ctx desx_ctx; +} ntfs_fek; + +struct options { + char *keyfile; /* .pfx file containing the user's private key. */ + char *device; /* Device/File to work with */ + char *file; /* File to display */ + s64 inode; /* Inode to work with */ + ATTR_TYPES attr; /* Attribute type to display */ + int force; /* Override common sense */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int encrypt; /* Encrypt */ +}; + +static const char *EXEC_NAME = "ntfsdecrypt"; +static struct options opts; + +static ntfschar EFS[5] = { + const_cpu_to_le16('$'), const_cpu_to_le16('E'), const_cpu_to_le16('F'), + const_cpu_to_le16('S'), const_cpu_to_le16('\0') +}; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Decrypt files and print on the " + "standard output.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2005 Yuval Fledel\n"); + ntfs_log_info("Copyright (c) 2005 Anton Altaparmakov\n"); + ntfs_log_info("Copyright (c) 2014 Jean-Pierre Andre\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] -k name.pfx device [file]\n\n" + " -i, --inode num Display this inode\n\n" + " -k --keyfile name.pfx Use file name as the user's private key file.\n" + " -e --encrypt Update an encrypted file\n" + " -f --force Use less caution\n" + " -h --help Print this help\n" + " -q --quiet Less output\n" + " -V --version Version information\n" + " -v --verbose More output\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-fh?ei:k:qVv"; + static const struct option lopt[] = { + {"encrypt", no_argument, NULL, 'e'}, + {"force", no_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"inode", required_argument, NULL, 'i'}, + {"keyfile", required_argument, NULL, 'k'}, + {"quiet", no_argument, NULL, 'q'}, + {"version", no_argument, NULL, 'V'}, + {"verbose", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0} + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.inode = -1; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) + opts.device = argv[optind - 1]; + else if (!opts.file) + opts.file = argv[optind - 1]; + else { + ntfs_log_error("You must specify exactly one " + "file.\n"); + err++; + } + break; + case 'e': + opts.encrypt++; + break; + case 'f': + opts.force++; + break; + case 'h': + help++; + break; + case 'k': + if (!opts.keyfile) + opts.keyfile = argv[optind - 1]; + else { + ntfs_log_error("You must specify exactly one " + "key file.\n"); + err++; + } + break; + case 'i': + if (opts.inode != -1) + ntfs_log_error("You must specify exactly one " + "inode.\n"); + else if (utils_parse_size(optarg, &opts.inode, FALSE)) + break; + else + ntfs_log_error("Couldn't parse inode number.\n"); + err++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'V': + ver++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case '?': + default: + ntfs_log_error("Unknown option '%s'.\n", + argv[optind - 1]); + err++; + break; + } + } + + if (help || ver) { + opts.quiet = 0; + ntfs_log_set_levels(NTFS_LOG_LEVEL_QUIET); + } else { + if (!opts.keyfile) { + ntfs_log_error("You must specify a key file.\n"); + err++; + } else if (opts.device == NULL) { + ntfs_log_error("You must specify a device.\n"); + err++; + } else if (opts.file == NULL && opts.inode == -1) { + ntfs_log_error("You must specify a file or inode with " + "the -i option.\n"); + err++; + } else if (opts.file != NULL && opts.inode != -1) { + ntfs_log_error("You can't specify both a file and " + "inode.\n"); + err++; + } + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose " + "at the same time.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + /* tri-state 0 : done, 1 : error, -1 : proceed */ + return (err ? 1 : (help || ver ? 0 : -1)); +} + +/** + * ntfs_pkcs12_load_pfxfile + */ +static int ntfs_pkcs12_load_pfxfile(const char *keyfile, u8 **pfx, + unsigned *pfx_size) +{ + int f, to_read, total, attempts, br; + struct stat key_stat; + + if (!keyfile || !pfx || !pfx_size) { + ntfs_log_error("You have to specify the key file, a pointer " + "to hold the key file contents, and a pointer " + "to hold the size of the key file contents.\n"); + return -1; + } + f = open(keyfile, O_RDONLY); + if (f == -1) { + ntfs_log_perror("Failed to open key file"); + return -1; + } + if (fstat(f, &key_stat) == -1) { + ntfs_log_perror("Failed to stat key file"); + goto file_out; + } + if (!S_ISREG(key_stat.st_mode)) { + ntfs_log_error("Key file is not a regular file, cannot read " + "it.\n"); + goto file_out; + } + if (!key_stat.st_size) { + ntfs_log_error("Key file has zero size.\n"); + goto file_out; + } + *pfx = malloc(key_stat.st_size + 1); + if (!*pfx) { + ntfs_log_perror("Failed to allocate buffer for key file " + "contents"); + goto file_out; + } + to_read = key_stat.st_size; + total = attempts = 0; + do { + br = read(f, *pfx + total, to_read); + if (br == -1) { + ntfs_log_perror("Failed to read from key file"); + goto free_out; + } + if (!br) + attempts++; + to_read -= br; + total += br; + } while (to_read > 0 && attempts < 3); + close(f); + /* Make sure it is zero terminated. */ + (*pfx)[key_stat.st_size] = 0; + *pfx_size = key_stat.st_size; + return 0; +free_out: + free(*pfx); +file_out: + close(f); + return -1; +} + +/** + * ntfs_crypto_init + */ +static int ntfs_crypto_init(void) +{ + int err; + + /* Initialize gcrypt library. Note: Must come before GNU TLS init. */ + if (gcry_control(GCRYCTL_DISABLE_SECMEM, 0) != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to initialize the gcrypt library.\n"); + return -1; + } + /* Initialize GNU TLS library. Note: Must come after libgcrypt init. */ + err = gnutls_global_init(); + if (err < 0) { + ntfs_log_error("Failed to initialize GNU TLS library: %s\n", + gnutls_strerror(err)); + return -1; + } + return 0; +} + +/** + * ntfs_crypto_deinit + */ +static void ntfs_crypto_deinit(void) +{ + gnutls_global_deinit(); +} + +/** + * ntfs_rsa_private_key_import_from_gnutls + */ +static ntfs_rsa_private_key ntfs_rsa_private_key_import_from_gnutls( + gnutls_x509_privkey_t priv_key) +{ + int i, j; + size_t tmp_size; + gnutls_datum_t rd[6]; + gcry_mpi_t rm[6]; + gcry_sexp_t rsa_key; + + /* Extract the RSA parameters from the GNU TLS private key. */ + if (gnutls_x509_privkey_export_rsa_raw(priv_key, &rd[0], &rd[1], + &rd[2], &rd[3], &rd[4], &rd[5])) { + ntfs_log_error("Failed to export rsa parameters. (Is the " + "key an RSA private key?)\n"); + return NULL; + } + /* Convert each RSA parameter to mpi format. */ + for (i = 0; i < 6; i++) { + if (gcry_mpi_scan(&rm[i], GCRYMPI_FMT_USG, rd[i].data, + rd[i].size, &tmp_size) != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to convert RSA parameter %i " + "to mpi format (size %d)\n", i, + rd[i].size); + rsa_key = NULL; + break; + } + } + /* Release the no longer needed datum values. */ + for (j = 0; j < 6; j++) { + if (rd[j].data && rd[j].size) + gnutls_free(rd[j].data); + } + /* + * Build the gcrypt private key, note libgcrypt uses p and q inversed + * to what gnutls uses. + */ + if (i == 6 && gcry_sexp_build(&rsa_key, NULL, + "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", + rm[0], rm[1], rm[2], rm[4], rm[3], rm[5]) != + GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to build RSA private key s-exp.\n"); + rsa_key = NULL; + } + /* Release the no longer needed mpi values. */ + for (j = 0; j < i; j++) + gcry_mpi_release(rm[j]); + return (ntfs_rsa_private_key)rsa_key; +} + +/** + * ntfs_rsa_private_key_release + */ +static void ntfs_rsa_private_key_release(ntfs_rsa_private_key rsa_key) +{ + gcry_sexp_release((gcry_sexp_t)rsa_key); +} + +/** + * ntfs_pkcs12_extract_rsa_key + */ +static ntfs_rsa_private_key ntfs_pkcs12_extract_rsa_key(u8 *pfx, int pfx_size, + char *password, char *thumbprint, int thumbprint_size, + NTFS_DF_TYPES *df_type) +{ + int err, bag_index, flags; + gnutls_datum_t dpfx, dkey; + gnutls_pkcs12_t pkcs12 = NULL; + gnutls_pkcs12_bag_t bag = NULL; + gnutls_x509_privkey_t pkey = NULL; + gnutls_x509_crt_t crt = NULL; + ntfs_rsa_private_key rsa_key = NULL; + char purpose_oid[100]; + size_t purpose_oid_size = sizeof(purpose_oid); + int oid_index; + size_t tp_size = thumbprint_size; + BOOL have_thumbprint = FALSE; + + *df_type = DF_TYPE_UNKNOWN; + /* Create a pkcs12 structure. */ + err = gnutls_pkcs12_init(&pkcs12); + if (err) { + ntfs_log_error("Failed to initialize PKCS#12 structure: %s\n", + gnutls_strerror(err)); + return NULL; + } + /* Convert the PFX file (DER format) to native pkcs12 format. */ + dpfx.data = pfx; + dpfx.size = pfx_size; + err = gnutls_pkcs12_import(pkcs12, &dpfx, GNUTLS_X509_FMT_DER, 0); + if (err) { + ntfs_log_error("Failed to convert the PFX file from DER to " + "native PKCS#12 format: %s\n", + gnutls_strerror(err)); + goto err; + } + /* + * Verify that the password is correct and that the key file has not + * been tampered with. Note if the password has zero length and the + * verification fails, retry with password set to NULL. This is needed + * to get passwordless .pfx files generated with Windows XP SP1 (and + * probably earlier versions of Windows) to work. + */ +retry_verify: + err = gnutls_pkcs12_verify_mac(pkcs12, password); + if (err) { + if (err == GNUTLS_E_MAC_VERIFY_FAILED && + password && !strlen(password)) { + password = NULL; + goto retry_verify; + } + ntfs_log_error("Failed to verify the MAC: %s Is the " + "password correct?\n", gnutls_strerror(err)); + goto err; + } + for (bag_index = 0; ; bag_index++) { + err = gnutls_pkcs12_bag_init(&bag); + if (err) { + ntfs_log_error("Failed to initialize PKCS#12 Bag " + "structure: %s\n", + gnutls_strerror(err)); + goto err; + } + err = gnutls_pkcs12_get_bag(pkcs12, bag_index, bag); + if (err) { + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + err = 0; + break; + } + ntfs_log_error("Failed to obtain Bag from PKCS#12 " + "structure: %s\n", + gnutls_strerror(err)); + goto err; + } +check_again: + err = gnutls_pkcs12_bag_get_count(bag); + if (err < 0) { + ntfs_log_error("Failed to obtain Bag count: %s\n", + gnutls_strerror(err)); + goto err; + } + err = gnutls_pkcs12_bag_get_type(bag, 0); + if (err < 0) { + ntfs_log_error("Failed to determine Bag type: %s\n", + gnutls_strerror(err)); + goto err; + } + flags = 0; + switch (err) { + case GNUTLS_BAG_PKCS8_KEY: + flags = GNUTLS_PKCS_PLAIN; + case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: + err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); + if (err < 0) { + ntfs_log_error("Failed to obtain Bag data: " + "%s\n", gnutls_strerror(err)); + goto err; + } + err = gnutls_x509_privkey_init(&pkey); + if (err) { + ntfs_log_error("Failed to initialized " + "private key structure: %s\n", + gnutls_strerror(err)); + goto err; + } + /* Decrypt the private key into GNU TLS format. */ + err = gnutls_x509_privkey_import_pkcs8(pkey, &dkey, + GNUTLS_X509_FMT_DER, password, flags); + if (err) { + ntfs_log_error("Failed to convert private " + "key from DER to GNU TLS " + "format: %s\n", + gnutls_strerror(err)); + goto err; + } +#if 0 + /* + * Export the key again, but unencrypted, and output it + * to stderr. Note the output has an RSA header so to + * compare to openssl pkcs12 -nodes -in myfile.pfx + * output need to ignore the part of the key between + * the first "MII..." up to the second "MII...". The + * actual RSA private key begins at the second "MII..." + * and in my testing at least was identical to openssl + * output and was also identical both on big and little + * endian so gnutls should be endianness safe. + */ + char *buf = malloc(8192); + size_t bufsize = 8192; + err = gnutls_x509_privkey_export_pkcs8(pkey, + GNUTLS_X509_FMT_PEM, "", GNUTLS_PKCS_PLAIN, buf, + &bufsize); + if (err) { + ntfs_log_error("eek1\n"); + exit(1); + } + ntfs_log_error("%s\n", buf); + free(buf); +#endif + /* Convert the private key to our internal format. */ + rsa_key = ntfs_rsa_private_key_import_from_gnutls(pkey); + if (!rsa_key) + goto err; + break; + case GNUTLS_BAG_ENCRYPTED: + err = gnutls_pkcs12_bag_decrypt(bag, password); + if (err) { + ntfs_log_error("Failed to decrypt Bag: %s\n", + gnutls_strerror(err)); + goto err; + } + goto check_again; + case GNUTLS_BAG_CERTIFICATE: + err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); + if (err < 0) { + ntfs_log_error("Failed to obtain Bag data: " + "%s\n", gnutls_strerror(err)); + goto err; + } + err = gnutls_x509_crt_init(&crt); + if (err) { + ntfs_log_error("Failed to initialize " + "certificate structure: %s\n", + gnutls_strerror(err)); + goto err; + } + err = gnutls_x509_crt_import(crt, &dkey, + GNUTLS_X509_FMT_DER); + if (err) { + ntfs_log_error("Failed to convert certificate " + "from DER to GNU TLS format: " + "%s\n", gnutls_strerror(err)); + goto err; + } + oid_index = 0; + /* + * Search in the key purposes for an EFS + * encryption purpose or an EFS recovery + * purpose, and use the first one found. + */ + do { + purpose_oid_size = sizeof(purpose_oid); + err = gnutls_x509_crt_get_key_purpose_oid(crt, + oid_index, + purpose_oid, &purpose_oid_size, NULL); + if (!err) { + purpose_oid[purpose_oid_size - 1] + = '\0'; + if (!strcmp(purpose_oid, + NTFS_EFS_CERT_PURPOSE_OID_DRF)) + *df_type = DF_TYPE_DRF; + else if (!strcmp(purpose_oid, + NTFS_EFS_CERT_PURPOSE_OID_DDF)) + *df_type = DF_TYPE_DDF; + else + oid_index++; + } + } while (!err && (*df_type == DF_TYPE_UNKNOWN)); + if (*df_type == DF_TYPE_UNKNOWN) { + /* End of list reached ? */ + if (err + == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + ntfs_log_error("Key does not have an " + "EFS purpose OID\n"); + else + ntfs_log_error("Failed to get a key " + "purpose OID : %s ", + gnutls_strerror(err)); + goto err; + } + /* Return the thumbprint to the caller. */ + err = gnutls_x509_crt_get_fingerprint(crt, + GNUTLS_DIG_SHA1, thumbprint, &tp_size); + if (err) { + ntfs_log_error("Failed to get thumbprint: " + "%s\n", gnutls_strerror(err)); + goto err; + } + if (tp_size != NTFS_SHA1_THUMBPRINT_SIZE) { + ntfs_log_error("Invalid thumbprint size %zd. " + "Should be %d.\n", tp_size, + thumbprint_size); + err = EINVAL; + goto err; + } + have_thumbprint = TRUE; + gnutls_x509_crt_deinit(crt); + crt = NULL; + break; + default: + /* We do not care about other types. */ + break; + } + gnutls_pkcs12_bag_deinit(bag); + } +err: + if (rsa_key && (err || *df_type == DF_TYPE_UNKNOWN || + !have_thumbprint)) { + if (!err) + ntfs_log_error("Key type or thumbprint not found, " + "aborting.\n"); + ntfs_rsa_private_key_release(rsa_key); + rsa_key = NULL; + } + if (crt) + gnutls_x509_crt_deinit(crt); + if (pkey) + gnutls_x509_privkey_deinit(pkey); + if (bag) + gnutls_pkcs12_bag_deinit(bag); + if (pkcs12) + gnutls_pkcs12_deinit(pkcs12); + return rsa_key; +} + +/** + * ntfs_buffer_reverse - + * + * This is a utility function for reversing the order of a buffer in place. + * Users of this function should be very careful not to sweep byte order + * problems under the rug. + */ +static inline void ntfs_buffer_reverse(u8 *buf, unsigned buf_size) +{ + unsigned i; + u8 t; + + for (i = 0; i < buf_size / 2; i++) { + t = buf[i]; + buf[i] = buf[buf_size - i - 1]; + buf[buf_size - i - 1] = t; + } +} + +#ifndef HAVE_STRNLEN +/** + * strnlen - strnlen is a gnu extension so emulate it if not present + */ +static size_t strnlen(const char *s, size_t maxlen) +{ + const char *p, *end; + + /* Look for a '\0' character. */ + for (p = s, end = s + maxlen; p < end && *p; p++) + ; + return p - s; +} +#endif /* ! HAVE_STRNLEN */ + +/** + * ntfs_raw_fek_decrypt - + * + * Note: decrypting into the input buffer. + */ +static unsigned ntfs_raw_fek_decrypt(u8 *fek, u32 fek_size, + ntfs_rsa_private_key rsa_key) +{ + gcry_mpi_t fek_mpi; + gcry_sexp_t fek_sexp, fek_sexp2; + gcry_error_t err; + size_t size, padding; + + /* Reverse the raw FEK. */ + ntfs_buffer_reverse(fek, fek_size); + /* Convert the FEK to internal MPI format. */ + err = gcry_mpi_scan(&fek_mpi, GCRYMPI_FMT_USG, fek, fek_size, NULL); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to convert file encryption key to " + "internal MPI format: %s\n", + gcry_strerror(err)); + return 0; + } + /* Create an internal S-expression from the FEK. */ + err = gcry_sexp_build(&fek_sexp, NULL, + "(enc-val (flags) (rsa (a %m)))", fek_mpi); + gcry_mpi_release(fek_mpi); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to create internal S-expression of " + "the file encryption key: %s\n", + gcry_strerror(err)); + return 0; + } + /* Decrypt the FEK. */ + err = gcry_pk_decrypt(&fek_sexp2, fek_sexp, (gcry_sexp_t)rsa_key); + gcry_sexp_release(fek_sexp); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to decrypt the file encryption key: " + "%s\n", gcry_strerror(err)); + return 0; + } + /* Extract the actual FEK from the decrypted raw S-expression. */ + fek_sexp = gcry_sexp_find_token(fek_sexp2, "value", 0); + gcry_sexp_release(fek_sexp2); + if (!fek_sexp) { + ntfs_log_error("Failed to find the decrypted file encryption " + "key in the internal S-expression.\n"); + return 0; + } + /* Convert the decrypted FEK S-expression into MPI format. */ + fek_mpi = gcry_sexp_nth_mpi(fek_sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release(fek_sexp); + if (!fek_mpi) { + ntfs_log_error("Failed to convert the decrypted file " + "encryption key S-expression to internal MPI " + "format.\n"); + return 0; + } + /* Convert the decrypted FEK from MPI format to binary data. */ + err = gcry_mpi_print(GCRYMPI_FMT_USG, fek, fek_size, &size, fek_mpi); + gcry_mpi_release(fek_mpi); + if (err != GPG_ERR_NO_ERROR || !size) { + ntfs_log_error("Failed to convert decrypted file encryption " + "key from internal MPI format to binary data: " + "%s\n", gcry_strerror(err)); + return 0; + } + /* + * Finally, remove the PKCS#1 padding and return the size of the + * decrypted FEK. + */ + padding = strnlen((char *)fek, size) + 1; + if (padding > size) { + ntfs_log_error("Failed to remove PKCS#1 padding from " + "decrypted file encryption key.\n"); + return 0; + } + size -= padding; + memmove(fek, fek + padding, size); + return size; +} + +/** + * ntfs_desx_key_expand - expand a 128-bit desx key to the needed 192-bit key + * @src: source buffer containing 128-bit key + * + * Expands the on-disk 128-bit desx key to the needed des key, the in-, and the + * out-whitening keys required to perform desx {de,en}cryption. + */ +static gcry_error_t ntfs_desx_key_expand(const u8 *src, u32 *des_key, + u64 *out_whitening, u64 *in_whitening) +{ + static const u8 *salt1 = (const u8*)"Dan Simon "; + static const u8 *salt2 = (const u8*)"Scott Field"; + static const int salt_len = 12; + gcry_md_hd_t hd1, hd2; + u32 *md; + gcry_error_t err; + + err = gcry_md_open(&hd1, GCRY_MD_MD5, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to open MD5 digest.\n"); + return err; + } + /* Hash the on-disk key. */ + gcry_md_write(hd1, src, 128 / 8); + /* Copy the current hash for efficiency. */ + err = gcry_md_copy(&hd2, hd1); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to copy MD5 digest object.\n"); + goto out; + } + /* Hash with the first salt and store the result. */ + gcry_md_write(hd1, salt1, salt_len); + md = (u32*)gcry_md_read(hd1, 0); + des_key[0] = md[0] ^ md[1]; + des_key[1] = md[2] ^ md[3]; + /* Hash with the second salt and store the result. */ + gcry_md_write(hd2, salt2, salt_len); + md = (u32*)gcry_md_read(hd2, 0); + *out_whitening = *(u64*)md; + *in_whitening = *(u64*)(md + 2); + gcry_md_close(hd2); +out: + gcry_md_close(hd1); + return err; +} + +/** + * ntfs_desx_decrypt + */ +static void ntfs_desx_decrypt(ntfs_fek *fek, u8 *outbuf, const u8 *inbuf) +{ + gcry_error_t err; + ntfs_desx_ctx *ctx = &fek->desx_ctx; + + err = gcry_cipher_reset(fek->gcry_cipher_hd); + if (err != GPG_ERR_NO_ERROR) + ntfs_log_error("Failed to reset des cipher (error 0x%x).\n", + err); + *(u64*)outbuf = *(const u64*)inbuf ^ ctx->out_whitening; + err = gcry_cipher_encrypt(fek->gcry_cipher_hd, outbuf, 8, NULL, 0); + if (err != GPG_ERR_NO_ERROR) + ntfs_log_error("Des decryption failed (error 0x%x).\n", err); + *(u64*)outbuf ^= ctx->in_whitening; +} + +/** + * ntfs_desx_encrypt + */ +static void ntfs_desx_encrypt(ntfs_fek *fek, u8 *outbuf, const u8 *inbuf) +{ + gcry_error_t err; + ntfs_desx_ctx *ctx = &fek->desx_ctx; + + err = gcry_cipher_reset(fek->gcry_cipher_hd); + if (err != GPG_ERR_NO_ERROR) + ntfs_log_error("Failed to reset des cipher (error 0x%x).\n", + err); + *(u64*)outbuf = *(const u64*)inbuf ^ ctx->in_whitening; + err = gcry_cipher_decrypt(fek->gcry_cipher_hd, outbuf, 8, NULL, 0); + if (err != GPG_ERR_NO_ERROR) + ntfs_log_error("Des decryption failed (error 0x%x).\n", err); + *(u64*)outbuf ^= ctx->out_whitening; +} + +//#define DO_CRYPTO_TESTS 1 + +#ifdef DO_CRYPTO_TESTS + +/* Do not remove this test code from this file! AIA */ +/** + * ntfs_desx_key_expand_test + */ +static BOOL ntfs_desx_key_expand_test(void) +{ + const u8 known_desx_on_disk_key[16] = { + 0xa1, 0xf9, 0xe0, 0xb2, 0x53, 0x23, 0x9e, 0x8f, + 0x0f, 0x91, 0x45, 0xd9, 0x8e, 0x20, 0xec, 0x30 + }; + const u8 known_des_key[8] = { + 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f, + }; + const u8 known_out_whitening[8] = { + 0xed, 0xda, 0x4c, 0x47, 0x60, 0x49, 0xdb, 0x8d, + }; + const u8 known_in_whitening[8] = { + 0x75, 0xf6, 0xa0, 0x1a, 0xc0, 0xca, 0x28, 0x1e + }; + u64 test_out_whitening, test_in_whitening; + union { + u64 u64; + u32 u32[2]; + } test_des_key; + gcry_error_t err; + BOOL res; + + err = ntfs_desx_key_expand(known_desx_on_disk_key, test_des_key.u32, + &test_out_whitening, &test_in_whitening); + if (err != GPG_ERR_NO_ERROR) + res = FALSE; + else + res = test_des_key.u64 == *(u64*)known_des_key && + test_out_whitening == + *(u64*)known_out_whitening && + test_in_whitening == + *(u64*)known_in_whitening; + ntfs_log_error("Testing whether ntfs_desx_key_expand() works: %s\n", + res ? "SUCCESS" : "FAILED"); + return res; +} + +/** + * ntfs_des_test + */ +static BOOL ntfs_des_test(void) +{ + const u8 known_des_key[8] = { + 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f + }; + const u8 known_des_encrypted_data[8] = { + 0xdc, 0xf7, 0x68, 0x2a, 0xaf, 0x48, 0x53, 0x0f + }; + const u8 known_decrypted_data[8] = { + 0xd8, 0xd9, 0x15, 0x23, 0x5b, 0x88, 0x0e, 0x09 + }; + u8 test_decrypted_data[8]; + int res; + gcry_error_t err; + gcry_cipher_hd_t gcry_cipher_hd; + + err = gcry_cipher_open(&gcry_cipher_hd, GCRY_CIPHER_DES, + GCRY_CIPHER_MODE_ECB, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to open des cipher (error 0x%x).\n", + err); + return FALSE; + } + err = gcry_cipher_setkey(gcry_cipher_hd, known_des_key, + sizeof(known_des_key)); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to set des key (error 0x%x.\n", err); + gcry_cipher_close(gcry_cipher_hd); + return FALSE; + } + /* + * Apply DES decryption (ntfs actually uses encryption when decrypting). + */ + err = gcry_cipher_encrypt(gcry_cipher_hd, test_decrypted_data, + sizeof(test_decrypted_data), known_des_encrypted_data, + sizeof(known_des_encrypted_data)); + gcry_cipher_close(gcry_cipher_hd); + if (err) { + ntfs_log_error("Failed to des decrypt test data (error " + "0x%x).\n", err); + return FALSE; + } + res = !memcmp(test_decrypted_data, known_decrypted_data, + sizeof(known_decrypted_data)); + ntfs_log_error("Testing whether des decryption works: %s\n", + res ? "SUCCESS" : "FAILED"); + return res; +} + +#else /* !defined(DO_CRYPTO_TESTS) */ + +/** + * ntfs_desx_key_expand_test + */ +static inline BOOL ntfs_desx_key_expand_test(void) +{ + return TRUE; +} + +/** + * ntfs_des_test + */ +static inline BOOL ntfs_des_test(void) +{ + return TRUE; +} + +#endif /* !defined(DO_CRYPTO_TESTS) */ + +/** + * ntfs_fek_import_from_raw + */ +static ntfs_fek *ntfs_fek_import_from_raw(u8 *fek_buf, unsigned fek_size) +{ + ntfs_fek *fek; + u32 key_size, wanted_key_size, gcry_algo; + int gcry_mode; + gcry_error_t err; + ntfs_desx_ctx *ctx; + + key_size = le32_to_cpup(fek_buf); + ntfs_log_debug("key_size 0x%x\n", key_size); + if (key_size + 16 > fek_size) { + ntfs_log_debug("Invalid FEK. It was probably decrypted with " + "the incorrect RSA key."); + errno = EINVAL; + return NULL; + } + fek = malloc(((((sizeof(*fek) + 7) & ~7) + key_size + 7) & ~7) + + sizeof(gcry_cipher_hd_t)); + if (!fek) { + errno = ENOMEM; + return NULL; + } + ctx = &fek->desx_ctx; + fek->alg_id = *(le32*)(fek_buf + 8); + //ntfs_log_debug("alg_id 0x%x\n", le32_to_cpu(fek->alg_id)); + fek->key_data = (u8*)fek + ((sizeof(*fek) + 7) & ~7); + memcpy(fek->key_data, fek_buf + 16, key_size); + fek->des_gcry_cipher_hd_ptr = NULL; + *(gcry_cipher_hd_t***)(fek->key_data + ((key_size + 7) & ~7)) = + &fek->des_gcry_cipher_hd_ptr; + switch (fek->alg_id) { + case CALG_DESX: + wanted_key_size = 16; + gcry_algo = GCRY_CIPHER_DES; + gcry_mode = GCRY_CIPHER_MODE_ECB; + break; + case CALG_3DES: + wanted_key_size = 24; + gcry_algo = GCRY_CIPHER_3DES; + gcry_mode = GCRY_CIPHER_MODE_CBC; + break; + case CALG_AES_256: + wanted_key_size = 32; + gcry_algo = GCRY_CIPHER_AES256; + gcry_mode = GCRY_CIPHER_MODE_CBC; + break; + default: + wanted_key_size = 8; + gcry_algo = GCRY_CIPHER_DES; + gcry_mode = GCRY_CIPHER_MODE_CBC; + if (fek->alg_id == CALG_DES) + ntfs_log_error("DES is not supported at present\n"); + else + ntfs_log_error("Unknown crypto algorithm 0x%x\n", + le32_to_cpu(fek->alg_id)); + ntfs_log_error(". Please email %s and say that you saw this " + "message. We will then try to implement " + "support for this algorithm.\n", NTFS_DEV_LIST); + err = EOPNOTSUPP; + goto out; + } + if (key_size != wanted_key_size) { + ntfs_log_error("%s key of %u bytes but needed size is %u " + "bytes, assuming corrupt or incorrect key. " + "Aborting.\n", + gcry_cipher_algo_name(gcry_algo), + (unsigned)key_size, (unsigned)wanted_key_size); + err = EIO; + goto out; + } + err = gcry_cipher_open(&fek->gcry_cipher_hd, gcry_algo, + gcry_mode, 0); + + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("gcry_cipher_open() failed: %s\n", + gcry_strerror(err)); + err = EINVAL; + goto out; + } + if (fek->alg_id == CALG_DESX) { + err = ntfs_desx_key_expand(fek->key_data, (u32*)ctx->des_key, + &ctx->out_whitening, &ctx->in_whitening); + if (err == GPG_ERR_NO_ERROR) + err = gcry_cipher_setkey(fek->gcry_cipher_hd, + ctx->des_key, 8); + } else { + err = gcry_cipher_setkey(fek->gcry_cipher_hd, fek->key_data, + key_size); + } + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("gcry_cipher_setkey() failed: %s\n", + gcry_strerror(err)); + gcry_cipher_close(fek->gcry_cipher_hd); + err = EINVAL; + goto out; + } + return fek; +out: + free(fek); + errno = err; + return NULL; +} + +/** + * ntfs_fek_release + */ +static void ntfs_fek_release(ntfs_fek *fek) +{ + if (fek->des_gcry_cipher_hd_ptr) + gcry_cipher_close(*fek->des_gcry_cipher_hd_ptr); + gcry_cipher_close(fek->gcry_cipher_hd); + free(fek); +} + +/** + * ntfs_df_array_fek_get + */ +static ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array, + ntfs_rsa_private_key rsa_key, char *thumbprint, + int thumbprint_size) +{ + EFS_DF_HEADER *df_header; + EFS_DF_CREDENTIAL_HEADER *df_cred; + EFS_DF_CERT_THUMBPRINT_HEADER *df_cert; + u8 *fek_buf; + ntfs_fek *fek; + u32 df_count, fek_size; + unsigned i; + + df_count = le32_to_cpu(df_array->df_count); + if (!df_count) + ntfs_log_error("There are no elements in the DF array.\n"); + df_header = (EFS_DF_HEADER*)(df_array + 1); + for (i = 0; i < df_count; i++, df_header = (EFS_DF_HEADER*)( + (u8*)df_header + le32_to_cpu(df_header->df_length))) { + df_cred = (EFS_DF_CREDENTIAL_HEADER*)((u8*)df_header + + le32_to_cpu(df_header->cred_header_offset)); + if (df_cred->type != NTFS_CRED_TYPE_CERT_THUMBPRINT) { + ntfs_log_debug("Credential type is not certificate " + "thumbprint, skipping DF entry.\n"); + continue; + } + df_cert = (EFS_DF_CERT_THUMBPRINT_HEADER*)((u8*)df_cred + + le32_to_cpu( + df_cred->cert_thumbprint_header_offset)); + if ((int)le32_to_cpu(df_cert->thumbprint_size) + != thumbprint_size) { + ntfs_log_error("Thumbprint size %d is not valid " + "(should be %d), skipping this DF " + "entry.\n", + le32_to_cpu(df_cert->thumbprint_size), + thumbprint_size); + continue; + } + if (memcmp((u8*)df_cert + + le32_to_cpu(df_cert->thumbprint_offset), + thumbprint, thumbprint_size)) { + ntfs_log_debug("Thumbprints do not match, skipping " + "this DF entry.\n"); + continue; + } + /* + * The thumbprints match so this is probably the DF entry + * matching the RSA key. Try to decrypt the FEK with it. + */ + fek_size = le32_to_cpu(df_header->fek_size); + fek_buf = (u8*)df_header + le32_to_cpu(df_header->fek_offset); + /* Decrypt the FEK. Note: This is done in place. */ + fek_size = ntfs_raw_fek_decrypt(fek_buf, fek_size, rsa_key); + if (fek_size) { + /* Convert the FEK to our internal format. */ + fek = ntfs_fek_import_from_raw(fek_buf, fek_size); + if (fek) + return fek; + ntfs_log_error("Failed to convert the decrypted file " + "encryption key to internal format.\n"); + } else + ntfs_log_error("Failed to decrypt the file " + "encryption key.\n"); + } + return NULL; +} + +/** + * ntfs_inode_fek_get - + */ +static ntfs_fek *ntfs_inode_fek_get(ntfs_inode *inode, + ntfs_rsa_private_key rsa_key, char *thumbprint, + int thumbprint_size, NTFS_DF_TYPES df_type) +{ + EFS_ATTR_HEADER *efs; + EFS_DF_ARRAY_HEADER *df_array = NULL; + ntfs_fek *fek = NULL; + + /* Obtain the $EFS contents. */ + efs = ntfs_attr_readall(inode, AT_LOGGED_UTILITY_STREAM, EFS, 4, NULL); + if (!efs) { + ntfs_log_perror("Failed to read $EFS attribute"); + return NULL; + } + /* + * Depending on whether the key is a normal key or a data recovery key, + * iterate through the DDF or DRF array, respectively. + */ + if (df_type == DF_TYPE_DDF) { + if (efs->offset_to_ddf_array) + df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + + le32_to_cpu(efs->offset_to_ddf_array)); + else + ntfs_log_error("There are no entries in the DDF " + "array.\n"); + } else if (df_type == DF_TYPE_DRF) { + if (efs->offset_to_drf_array) + df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + + le32_to_cpu(efs->offset_to_drf_array)); + else + ntfs_log_error("There are no entries in the DRF " + "array.\n"); + } else + ntfs_log_error("Invalid DF type.\n"); + if (df_array) + fek = ntfs_df_array_fek_get(df_array, rsa_key, thumbprint, + thumbprint_size); + free(efs); + return fek; +} + +/** + * ntfs_fek_decrypt_sector + */ +static int ntfs_fek_decrypt_sector(ntfs_fek *fek, u8 *data, const u64 offset) +{ + gcry_error_t err; + + err = gcry_cipher_reset(fek->gcry_cipher_hd); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to reset cipher: %s\n", + gcry_strerror(err)); + return -1; + } + /* + * Note: You may wonder why we are not calling gcry_cipher_setiv() here + * instead of doing it by hand after the decryption. The answer is + * that gcry_cipher_setiv() wants an iv of length 8 bytes but we give + * it a length of 16 for AES256 so it does not like it. + */ + if (fek->alg_id == CALG_DESX) { + int k; + + for (k=0; k<512; k+=8) { + ntfs_desx_decrypt(fek, &data[k], &data[k]); + } + } else + err = gcry_cipher_decrypt(fek->gcry_cipher_hd, data, 512, NULL, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Decryption failed: %s\n", gcry_strerror(err)); + return -1; + } + /* Apply the IV. */ + if (fek->alg_id == CALG_AES_256) { + ((le64*)data)[0] ^= cpu_to_le64(0x5816657be9161312ULL + offset); + ((le64*)data)[1] ^= cpu_to_le64(0x1989adbe44918961ULL + offset); + } else { + /* All other algos (Des, 3Des, DesX) use the same IV. */ + ((le64*)data)[0] ^= cpu_to_le64(0x169119629891ad13ULL + offset); + } + return 512; +} + +/** + * ntfs_fek_encrypt_sector + */ +static int ntfs_fek_encrypt_sector(ntfs_fek *fek, u8 *data, const u64 offset) +{ + gcry_error_t err; + + err = gcry_cipher_reset(fek->gcry_cipher_hd); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to reset cipher: %s\n", + gcry_strerror(err)); + return -1; + } + /* + * Note: You may wonder why we are not calling gcry_cipher_setiv() here + * instead of doing it by hand after the decryption. The answer is + * that gcry_cipher_setiv() wants an iv of length 8 bytes but we give + * it a length of 16 for AES256 so it does not like it. + */ + /* Apply the IV. */ + if (fek->alg_id == CALG_AES_256) { + ((le64*)data)[0] ^= cpu_to_le64(0x5816657be9161312ULL + offset); + ((le64*)data)[1] ^= cpu_to_le64(0x1989adbe44918961ULL + offset); + } else { + /* All other algos (Des, 3Des, DesX) use the same IV. */ + ((le64*)data)[0] ^= cpu_to_le64(0x169119629891ad13ULL + offset); + } + if (fek->alg_id == CALG_DESX) { + int k; + + for (k=0; k<512; k+=8) { + ntfs_desx_encrypt(fek, &data[k], &data[k]); + } + } else + err = gcry_cipher_encrypt(fek->gcry_cipher_hd, data, 512, NULL, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Encryption failed: %s\n", gcry_strerror(err)); + return -1; + } + return 512; +} + +/** + * ntfs_cat_decrypt - Decrypt the contents of an encrypted file to stdout. + * @inode: An encrypted file's inode structure, as obtained by + * ntfs_inode_open(). + * @fek: A file encryption key. As obtained by ntfs_inode_fek_get(). + */ +static int ntfs_cat_decrypt(ntfs_inode *inode, ntfs_fek *fek) +{ + int bufsize = 512; + unsigned char *buffer; + ntfs_attr *attr; + s64 bytes_read, written, offset, total; + s64 old_data_size, old_initialized_size; + int i; + + buffer = malloc(bufsize); + if (!buffer) + return 1; + attr = ntfs_attr_open(inode, AT_DATA, NULL, 0); + if (!attr) { + ntfs_log_error("Cannot cat a directory.\n"); + free(buffer); + return 1; + } + total = attr->data_size; + + // hack: make sure attr will not be commited to disk if you use this. + // clear the encrypted bit, otherwise the library won't allow reading. + NAttrClearEncrypted(attr); + // extend the size, we may need to read past the end of the stream. + old_data_size = attr->data_size; + old_initialized_size = attr->initialized_size; + attr->data_size = attr->initialized_size = attr->allocated_size; + + offset = 0; + while (total > 0) { + bytes_read = ntfs_attr_pread(attr, offset, 512, buffer); + if (bytes_read == -1) { + ntfs_log_perror("ERROR: Couldn't read file"); + break; + } + if (!bytes_read) + break; + if ((i = ntfs_fek_decrypt_sector(fek, buffer, offset)) < + bytes_read) { + ntfs_log_perror("ERROR: Couldn't decrypt all data!"); + ntfs_log_error("%u/%lld/%lld/%lld\n", i, + (long long)bytes_read, (long long)offset, + (long long)total); + break; + } + if (bytes_read > total) + bytes_read = total; + written = fwrite(buffer, 1, bytes_read, stdout); + if (written != bytes_read) { + ntfs_log_perror("ERROR: Couldn't output all data!"); + break; + } + offset += bytes_read; + total -= bytes_read; + } + attr->data_size = old_data_size; + attr->initialized_size = old_initialized_size; + NAttrSetEncrypted(attr); + ntfs_attr_close(attr); + free(buffer); + return 0; +} + +/** + * ntfs_feed_encrypt - Encrypt the contents of stdin to an encrypted file + * @inode: An encrypted file's inode structure, as obtained by + * ntfs_inode_open(). + * @fek: A file encryption key. As obtained by ntfs_inode_fek_get(). + */ +static int ntfs_feed_encrypt(ntfs_inode *inode, ntfs_fek *fek) +{ + const int bufsize = 512; + unsigned char *buffer; + ntfs_attr *attr; + s64 bytes_read, written, offset, total; + unsigned char *b; + long val; + int count; + int i; + + buffer = (unsigned char*)malloc(bufsize); + if (!buffer) + return 1; + attr = ntfs_attr_open(inode, AT_DATA, NULL, 0); + if (!attr) { + ntfs_log_error("Cannot feed into a directory.\n"); + goto rejected; + } + total = 0; + + if (!(attr->data_flags & ATTR_IS_ENCRYPTED)) { + ntfs_log_error("The data stream was not encrypted\n"); + goto rejected; + } + inode->vol->efs_raw = TRUE; + + if (ntfs_attr_truncate(attr, 0)) { + ntfs_log_error("Failed to truncate the data stream\n"); + goto rejected; + } + offset = 0; + do { + bytes_read = fread(buffer, 1, bufsize, stdin); + if (bytes_read <= 0) { + if (bytes_read < 0) + ntfs_log_perror("ERROR: Couldn't read data"); + } else { + if (bytes_read < bufsize) { + /* Fill with random data */ + srandom((unsigned int)(sle64_to_cpu( + inode->last_data_change_time) + /100000000)); + count = bufsize - bytes_read; + b = &buffer[bytes_read]; + do { + val = random(); + switch (count) { + default : + *b++ = val; + val >>= 8; + case 3 : + *b++ = val; + val >>= 8; + case 2 : + *b++ = val; + val >>= 8; + case 1 : + *b++ = val; + val >>= 8; + } + count -= 4; + } while (count > 0); + } + if ((i = ntfs_fek_encrypt_sector(fek, buffer, offset)) + < bufsize) { + ntfs_log_perror("ERROR: Couldn't encrypt all data!"); + ntfs_log_error("%u/%lld/%lld/%lld\n", i, + (long long)bytes_read, (long long)offset, + (long long)total); + break; + } + written = ntfs_attr_pwrite(attr, offset, bufsize, buffer); + if (written != bufsize) { + ntfs_log_perror("ERROR: Couldn't output all data!"); + break; + } + offset += bufsize; + total += bytes_read; + } + } while (bytes_read == bufsize); + ntfs_attr_truncate(attr, total); + inode->last_data_change_time = ntfs_current_time(); + NAttrSetEncrypted(attr); + ntfs_attr_close(attr); + free(buffer); + return 0; +rejected : + free(buffer); + return (-1); +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + u8 *pfx_buf; + char *password; + ntfs_rsa_private_key rsa_key; + ntfs_volume *vol; + ntfs_inode *inode; + ntfs_fek *fek; + unsigned pfx_size; + int res; + NTFS_DF_TYPES df_type; + char thumbprint[NTFS_SHA1_THUMBPRINT_SIZE]; + + ntfs_log_set_handler(ntfs_log_handler_stderr); + + res = parse_options(argc, argv); + if (res >= 0) + return (res); + utils_set_locale(); + + /* Initialize crypto in ntfs. */ + if (ntfs_crypto_init()) { + ntfs_log_error("Failed to initialize crypto. Aborting.\n"); + return 1; + } + /* Load the PKCS#12 (.pfx) file containing the user's private key. */ + if (ntfs_pkcs12_load_pfxfile(opts.keyfile, &pfx_buf, &pfx_size)) { + ntfs_log_error("Failed to load key file. Aborting.\n"); + ntfs_crypto_deinit(); + return 1; + } + /* Ask the user for their password. */ + password = getpass("Enter the password with which the private key was " + "encrypted: "); + if (!password) { + ntfs_log_perror("Failed to obtain user password"); + free(pfx_buf); + ntfs_crypto_deinit(); + return 1; + } + /* Obtain the user's private RSA key from the key file. */ + rsa_key = ntfs_pkcs12_extract_rsa_key(pfx_buf, pfx_size, password, + thumbprint, sizeof(thumbprint), &df_type); + /* Destroy the password. */ + memset(password, 0, strlen(password)); + /* No longer need the pfx file contents. */ + free(pfx_buf); + if (!rsa_key) { + ntfs_log_error("Failed to extract the private RSA key.\n"); + ntfs_crypto_deinit(); + return 1; + } + /* Mount the ntfs volume. */ + vol = utils_mount_volume(opts.device, + (opts.encrypt ? 0 : NTFS_MNT_RDONLY) | + (opts.force ? NTFS_MNT_RECOVER : 0)); + if (!vol) { + ntfs_log_error("Failed to mount ntfs volume. Aborting.\n"); + ntfs_rsa_private_key_release(rsa_key); + ntfs_crypto_deinit(); + return 1; + } + /* Open the encrypted ntfs file. */ + if (opts.inode != -1) + inode = ntfs_inode_open(vol, opts.inode); + else + inode = ntfs_pathname_to_inode(vol, NULL, opts.file); + if (!inode) { + ntfs_log_error("Failed to open encrypted file. Aborting.\n"); + ntfs_umount(vol, FALSE); + ntfs_rsa_private_key_release(rsa_key); + ntfs_crypto_deinit(); + return 1; + } + /* Obtain the file encryption key of the encrypted file. */ + fek = ntfs_inode_fek_get(inode, rsa_key, thumbprint, + sizeof(thumbprint), df_type); + ntfs_rsa_private_key_release(rsa_key); + if (fek) { + if (opts.encrypt) + res = ntfs_feed_encrypt(inode, fek); + else + res = ntfs_cat_decrypt(inode, fek); + ntfs_fek_release(fek); + } else { + ntfs_log_error("Failed to obtain file encryption key. " + "Aborting.\n"); + res = 1; + } + ntfs_inode_close(inode); + ntfs_umount(vol, FALSE); + ntfs_crypto_deinit(); + return res; +} diff --git a/ntfsprogs/ntfsdump_logfile.c b/ntfsprogs/ntfsdump_logfile.c new file mode 100755 index 0000000000000000000000000000000000000000..279ebac2bdb611bab58771c9a03fc4eb4431a394 --- /dev/null +++ b/ntfsprogs/ntfsdump_logfile.c @@ -0,0 +1,779 @@ +/** + * ntfsdump_logfile - Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2005 Anton Altaparmakov + * + * This utility will interpret the contents of the journal ($LogFile) of an + * NTFS partition and display the results on stdout. Errors will be output to + * stderr. + * + * 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 (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* TODO: + * - Remove the need for clipping at 64MiB. + * - Add normal command line switchs (use getopt_long()). + * - For a volume: allow dumping only uncommitted records. + * - For a file: get an optional command line parameter for the last SN. + * - Sanity checks. + */ + +#include "config.h" + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include "types.h" +#include "endians.h" +#include "volume.h" +#include "inode.h" +#include "attrib.h" +#include "layout.h" +#include "logfile.h" +#include "mst.h" +#include "utils.h" +/* #include "version.h" */ +#include "logging.h" + +typedef struct { + BOOL is_volume; + const char *filename; + s64 data_size; + union { + struct { + ntfs_volume *vol; + ntfs_inode *ni; + ntfs_attr *na; + }; + struct { + int fd; + }; + }; +} logfile_file; + +/** + * logfile_close + */ +static int logfile_close(logfile_file *logfile) +{ + if (logfile->is_volume) { + if (logfile->na) + ntfs_attr_close(logfile->na); + if (logfile->ni && ntfs_inode_close(logfile->ni)) + ntfs_log_perror("Warning: Failed to close $LogFile " + "(inode %i)", FILE_LogFile); + if (ntfs_umount(logfile->vol, 0)) + ntfs_log_perror("Warning: Failed to umount %s", + logfile->filename); + } else { + if (close(logfile->fd)) + ntfs_log_perror("Warning: Failed to close file %s", + logfile->filename); + } + return 0; +} + +/** + * device_err_exit - put an error message, cleanup and exit. + * @vol: volume to unmount. + * @ni: Inode to free. + * @na: Attribute to close. + * + * Use when you wish to exit and collate all the cleanups together. + * if you don't have some parameter to pass, just pass NULL. + */ +__attribute__((noreturn)) +__attribute__((format(printf, 4, 5))) +static void device_err_exit(ntfs_volume *vol, ntfs_inode *ni, + ntfs_attr *na, const char *fmt, ...) +{ + va_list ap; + + if (na) + ntfs_attr_close(na); + if (ni && ntfs_inode_close(ni)) + ntfs_log_perror("Warning: Failed to close $LogFile (inode %i)", + FILE_LogFile); + if (ntfs_umount(vol, 0)) + ntfs_log_perror("Warning: Failed to umount"); + + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + ntfs_log_error("Aborting...\n"); + exit(1); +} + +/** + * log_err_exit - + */ +__attribute__((noreturn)) +__attribute__((format(printf, 2, 3))) +static void log_err_exit(u8 *buf, const char *fmt, ...) +{ + va_list ap; + + free(buf); + + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + ntfs_log_error("Aborting...\n"); + exit(1); +} + +/** + * usage - + */ +__attribute__((noreturn)) +static void usage(const char *exec_name) +{ + ntfs_log_error("%s v%s (libntfs-3g) - Interpret and display information " + "about the journal\n($LogFile) of an NTFS volume.\n" + "Copyright (c) 2000-2005 Anton Altaparmakov.\n" + "%s is free software, released under the GNU General " + "Public License\nand you are welcome to redistribute " + "it under certain conditions.\n%s comes with " + "ABSOLUTELY NO WARRANTY; for details read the GNU\n" + "General Public License to be found in the file " + "COPYING in the main Linux-NTFS\ndistribution " + "directory.\nUsage: %s device\n e.g. %s /dev/hda6\n" + "Alternative usage: %s -f file\n e.g. %s -f " + "MyCopyOfTheLogFile\n", exec_name, VERSION, + exec_name, exec_name, + exec_name, exec_name, exec_name, exec_name); + exit(1); +} + +/** + * logfile_open + */ +static int logfile_open(BOOL is_volume, const char *filename, + logfile_file *logfile) +{ + if (is_volume) { + ntfs_volume *vol; + ntfs_inode *ni; + ntfs_attr *na; + + /* Porting note: NTFS_MNT_FORENSIC is not needed when we mount + * the volume in read-only mode. No changes will be made to the + * logfile or anything else when we are in read only-mode. */ + vol = ntfs_mount(filename, NTFS_MNT_RDONLY); + if (!vol) + log_err_exit(NULL, "Failed to mount %s: %s\n", + filename, strerror(errno)); + ntfs_log_info("Mounted NTFS volume %s (NTFS v%i.%i) on device %s.\n", + vol->vol_name ? vol->vol_name : "<NO_NAME>", + vol->major_ver, vol->minor_ver, filename); + if (ntfs_version_is_supported(vol)) + device_err_exit(vol, NULL, NULL, + "Unsupported NTFS version.\n"); + ni = ntfs_inode_open(vol, FILE_LogFile); + if (!ni) + device_err_exit(vol, NULL, NULL, "Failed to " + "open $LogFile (inode %i): %s\n", + FILE_LogFile, strerror(errno)); + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) + device_err_exit(vol, ni, NULL, "Failed to open " + "$LogFile/$DATA (attribute 0x%x):" + " %s\n", (unsigned int) + le32_to_cpu(AT_DATA), strerror(errno)); + if (!na->data_size) + device_err_exit(vol, ni, na, "$LogFile has zero " + "length. Run chkdsk /f to correct " + "this.\n"); + logfile->data_size = na->data_size; + logfile->vol = vol; + logfile->ni = ni; + logfile->na = na; + } else { + struct stat sbuf; + int fd; + + if (stat(filename, &sbuf) == -1) { + if (errno == ENOENT) + log_err_exit(NULL, "The file %s does not " + "exist. Did you specify it " + "correctly?\n", filename); + log_err_exit(NULL, "Error getting information about " + "%s: %s\n", filename, strerror(errno)); + } + + fd = open(filename, O_RDONLY); + if (fd == -1) + log_err_exit(NULL, "Failed to open file %s: %s\n", + filename, strerror(errno)); + logfile->data_size = sbuf.st_size; + logfile->fd = fd; + } + + logfile->is_volume = is_volume; + logfile->filename = filename; + + return 0; +} + +/** + * logfile_read + */ +static int logfile_pread(logfile_file *logfile, int ofs, int count, u8 *buf) +{ + int br; + + if (logfile->is_volume) { + br = (int)ntfs_attr_pread(logfile->na, ofs, count, buf); + } else { + if (lseek(logfile->fd, ofs, SEEK_SET)==-1) { + ntfs_log_error("Could not seek to offset %u\n", ofs); + return 0; + } + br = read(logfile->fd, buf, count); + } + if (br != count) { + ntfs_log_error("Only %d out of %d bytes read starting at %d\n", + br, count, ofs); + } + return br; +} + +/** + * restart_header_sanity() + */ +static void restart_header_sanity(RESTART_PAGE_HEADER *rstr, u8 *buf) +{ + unsigned int usa_end_ofs, page_size; + + /* Only CHKD records are allowed to have chkdsk_lsn set. */ + if (!ntfs_is_chkd_record(rstr->magic) && + sle64_to_cpu(rstr->chkdsk_lsn)) + log_err_exit(buf, "$LogFile is corrupt: Restart page header " + "magic is not CHKD but a chkdsk LSN is " + "specified. Cannot handle this yet.\n"); + /* Both system and log page size must be >= 512 and a power of 2. */ + page_size = le32_to_cpu(rstr->log_page_size); + if (page_size < 512 || page_size & (page_size - 1)) + log_err_exit(buf, "$LogFile is corrupt: Restart page header " + "specifies invalid log page size. Cannot " + "handle this yet.\n"); + if (page_size != le32_to_cpu(rstr->system_page_size)) { + page_size = le32_to_cpu(rstr->system_page_size); + if (page_size < 512 || page_size & (page_size - 1)) + log_err_exit(buf, "$LogFile is corrupt: Restart page " + "header specifies invalid system page " + "size. Cannot handle this yet.\n"); + } + /* Abort if the version number is not 1.1. */ + if (sle16_to_cpu(rstr->major_ver != 1) || + sle16_to_cpu(rstr->minor_ver != 1)) + log_err_exit(buf, "Unknown $LogFile version %i.%i. Only know " + "how to handle version 1.1.\n", + sle16_to_cpu(rstr->major_ver), + sle16_to_cpu(rstr->minor_ver)); + /* Verify the location and size of the update sequence array. */ + usa_end_ofs = le16_to_cpu(rstr->usa_ofs) + + le16_to_cpu(rstr->usa_count) * sizeof(u16); + if (page_size / NTFS_BLOCK_SIZE + 1 != le16_to_cpu(rstr->usa_count)) + log_err_exit(buf, "Restart page header in $LogFile is " + "corrupt: Update sequence array size is " + "wrong. Cannot handle this yet.\n"); + if (le16_to_cpu(rstr->usa_ofs) < sizeof(RESTART_PAGE_HEADER)) + log_err_exit(buf, "Restart page header in $LogFile is " + "corrupt: Update sequence array overlaps " + "restart page header. Cannot handle this " + "yet.\n"); + if (usa_end_ofs > NTFS_BLOCK_SIZE - sizeof(u16)) + log_err_exit(buf, "Restart page header in $LogFile is " + "corrupt: Update sequence array overlaps or " + "is behind first protected sequence number. " + "Cannot handle this yet.\n"); + if (usa_end_ofs > le16_to_cpu(rstr->restart_area_offset)) + log_err_exit(buf, "Restart page header in $LogFile is " + "corrupt: Update sequence array overlaps or " + "is behind restart area. Cannot handle this " + "yet.\n"); + /* Finally, verify the offset of the restart area. */ + if (le16_to_cpu(rstr->restart_area_offset) & 7) + log_err_exit(buf, "Restart page header in $LogFile is " + "corrupt: Restart area offset is not aligned " + "to 8-byte boundary. Cannot handle this " + "yet.\n"); +} + +/** + * dump_restart_areas_header + */ +static void dump_restart_areas_header(RESTART_PAGE_HEADER *rstr) +{ + ntfs_log_info("\nRestart page header:\n"); + ntfs_log_info("magic = %s\n", ntfs_is_rstr_record(rstr->magic) ? "RSTR" : + "CHKD"); + ntfs_log_info("usa_ofs = %u (0x%x)\n", le16_to_cpu(rstr->usa_ofs), + le16_to_cpu(rstr->usa_ofs)); + ntfs_log_info("usa_count = %u (0x%x)\n", le16_to_cpu(rstr->usa_count), + le16_to_cpu(rstr->usa_count)); + ntfs_log_info("chkdsk_lsn = %lli (0x%llx)\n", + (long long)sle64_to_cpu(rstr->chkdsk_lsn), + (unsigned long long)sle64_to_cpu(rstr->chkdsk_lsn)); + ntfs_log_info("system_page_size = %u (0x%x)\n", + (unsigned int)le32_to_cpu(rstr->system_page_size), + (unsigned int)le32_to_cpu(rstr->system_page_size)); + ntfs_log_info("log_page_size = %u (0x%x)\n", + (unsigned int)le32_to_cpu(rstr->log_page_size), + (unsigned int)le32_to_cpu(rstr->log_page_size)); + ntfs_log_info("restart_offset = %u (0x%x)\n", + le16_to_cpu(rstr->restart_area_offset), + le16_to_cpu(rstr->restart_area_offset)); +} + +/** + * dump_restart_areas_area + */ +static void dump_restart_areas_area(RESTART_PAGE_HEADER *rstr) +{ + LOG_CLIENT_RECORD *lcr; + RESTART_AREA *ra; + int client; + + ra = (RESTART_AREA*)((u8*)rstr + + le16_to_cpu(rstr->restart_area_offset)); + ntfs_log_info("current_lsn = %lli (0x%llx)\n", + (long long)sle64_to_cpu(ra->current_lsn), + (unsigned long long)sle64_to_cpu(ra->current_lsn)); + ntfs_log_info("log_clients = %u (0x%x)\n", le16_to_cpu(ra->log_clients), + le16_to_cpu(ra->log_clients)); + ntfs_log_info("client_free_list = %i (0x%x)\n", + (s16)le16_to_cpu(ra->client_free_list), + le16_to_cpu(ra->client_free_list)); + ntfs_log_info("client_in_use_list = %i (0x%x)\n", + (s16)le16_to_cpu(ra->client_in_use_list), + le16_to_cpu(ra->client_in_use_list)); + ntfs_log_info("flags = 0x%.4x\n", le16_to_cpu(ra->flags)); + ntfs_log_info("seq_number_bits = %u (0x%x)\n", + (unsigned int)le32_to_cpu(ra->seq_number_bits), + (unsigned int)le32_to_cpu(ra->seq_number_bits)); + ntfs_log_info("restart_area_length = %u (0x%x)\n", + le16_to_cpu(ra->restart_area_length), + le16_to_cpu(ra->restart_area_length)); + ntfs_log_info("client_array_offset = %u (0x%x)\n", + le16_to_cpu(ra->client_array_offset), + le16_to_cpu(ra->client_array_offset)); + ntfs_log_info("file_size = %lli (0x%llx)\n", + (long long)sle64_to_cpu(ra->file_size), + (unsigned long long)sle64_to_cpu(ra->file_size)); + ntfs_log_info("last_lsn_data_length = %u (0x%x)\n", + (unsigned int)le32_to_cpu(ra->last_lsn_data_length), + (unsigned int)le32_to_cpu(ra->last_lsn_data_length)); + ntfs_log_info("log_record_header_length = %u (0x%x)\n", + le16_to_cpu(ra->log_record_header_length), + le16_to_cpu(ra->log_record_header_length)); + ntfs_log_info("log_page_data_offset = %u (0x%x)\n", + le16_to_cpu(ra->log_page_data_offset), + le16_to_cpu(ra->log_page_data_offset)); + ntfs_log_info("restart_log_open_count = %u (0x%x)\n", + (unsigned)le32_to_cpu(ra->restart_log_open_count), + (unsigned)le32_to_cpu(ra->restart_log_open_count)); + lcr = (LOG_CLIENT_RECORD*)((u8*)ra + + le16_to_cpu(ra->client_array_offset)); + for (client = 0; client < le16_to_cpu(ra->log_clients); client++) { + char *client_name; + + ntfs_log_info("\nLog client record number %i:\n", client + 1); + ntfs_log_info("oldest_lsn = %lli (0x%llx)\n", + (long long)sle64_to_cpu(lcr->oldest_lsn), + (unsigned long long) + sle64_to_cpu(lcr->oldest_lsn)); + ntfs_log_info("client_restart_lsn = %lli (0x%llx)\n", (long long) + sle64_to_cpu(lcr->client_restart_lsn), + (unsigned long long) + sle64_to_cpu(lcr->client_restart_lsn)); + ntfs_log_info("prev_client = %i (0x%x)\n", + (s16)le16_to_cpu(lcr->prev_client), + le16_to_cpu(lcr->prev_client)); + ntfs_log_info("next_client = %i (0x%x)\n", + (s16)le16_to_cpu(lcr->next_client), + le16_to_cpu(lcr->next_client)); + ntfs_log_info("seq_number = %u (0x%x)\n", le16_to_cpu(lcr->seq_number), + le16_to_cpu(lcr->seq_number)); + ntfs_log_info("client_name_length = %u (0x%x)\n", + (unsigned int)le32_to_cpu(lcr->client_name_length) / 2, + (unsigned int)le32_to_cpu(lcr->client_name_length) / 2); + if (le32_to_cpu(lcr->client_name_length)) { + client_name = NULL; + if (ntfs_ucstombs(lcr->client_name, + le32_to_cpu(lcr->client_name_length) / + 2, &client_name, 0) < 0) { + ntfs_log_perror("Failed to convert log client name"); + client_name = strdup("<conversion error>"); + } + } else + client_name = strdup("<unnamed>"); + ntfs_log_info("client_name = %s\n", client_name); + free(client_name); + /* + * Log client records are fixed size so we can simply use the + * C increment operator to get to the next one. + */ + lcr++; + } +} + +/** + * dump_restart_areas() + */ +static void *dump_restart_areas(RESTART_PAGE_HEADER *rstr, u8 *buf, + unsigned int page_size) +{ + int pass = 1; + +rstr_pass_loc: + if (ntfs_is_chkd_record(rstr->magic)) + log_err_exit(buf, "The %s restart page header in $LogFile has " + "been modified by chkdsk. Do not know how to " + "handle this yet. Reboot into Windows to fix " + "this.\n", (u8*)rstr == buf ? "first" : + "second"); + if (ntfs_mst_post_read_fixup((NTFS_RECORD*)rstr, page_size) || + ntfs_is_baad_record(rstr->magic)) + log_err_exit(buf, "$LogFile incomplete multi sector transfer " + "detected in restart page header. Cannot " + "handle this yet.\n"); + if (pass == 1) + ntfs_log_info("$LogFile version %i.%i.\n", + sle16_to_cpu(rstr->major_ver), + sle16_to_cpu(rstr->minor_ver)); + else /* if (pass == 2) */ { + RESTART_AREA *ra; + + /* + * rstr is now the second restart page so we declare rstr1 + * as the first restart page as this one has been verified in + * the first pass so we can use all its members safely. + */ + RESTART_PAGE_HEADER *rstr1 = (RESTART_PAGE_HEADER*)buf; + + /* Exclude the usa from the comparison. */ + ra = (RESTART_AREA*)((u8*)rstr1 + + le16_to_cpu(rstr1->restart_area_offset)); + if (!memcmp(rstr1, rstr, le16_to_cpu(rstr1->usa_ofs)) && + !memcmp((u8*)rstr1 + le16_to_cpu( + rstr1->restart_area_offset), (u8*)rstr + + le16_to_cpu(rstr->restart_area_offset), + le16_to_cpu(ra->restart_area_length))) { + puts("\nSkipping analysis of second restart page " + "because it fully matches the first " + "one."); + goto skip_rstr_pass; + } + /* + * The $LogFile versions specified in each of the two restart + * page headers must match. + */ + if (rstr1->major_ver != rstr->major_ver || + rstr1->minor_ver != rstr->minor_ver) + log_err_exit(buf, "Second restart area specifies " + "different $LogFile version to first " + "restart area. Cannot handle this " + "yet.\n"); + } + /* The restart page header is in rstr and it is mst deprotected. */ + ntfs_log_info("\n%s restart page:\n", pass == 1 ? "1st" : "2nd"); + dump_restart_areas_header(rstr); + + ntfs_log_info("\nRestart area:\n"); + dump_restart_areas_area(rstr); + +skip_rstr_pass: + if (pass == 1) { + rstr = (RESTART_PAGE_HEADER*)((u8*)rstr + page_size); + ++pass; + goto rstr_pass_loc; + } + + return rstr; +} + +/** + * dump_log_records() + */ +static void dump_log_record(LOG_RECORD *lr) +{ + unsigned int i; + ntfs_log_info("this lsn = 0x%llx\n", + (unsigned long long)le64_to_cpu(lr->this_lsn)); + ntfs_log_info("client previous lsn = 0x%llx\n", (unsigned long long) + le64_to_cpu(lr->client_previous_lsn)); + ntfs_log_info("client undo next lsn = 0x%llx\n", (unsigned long long) + le64_to_cpu(lr->client_undo_next_lsn)); + ntfs_log_info("client data length = 0x%x\n", + (unsigned int)le32_to_cpu(lr->client_data_length)); + ntfs_log_info("client_id.seq_number = 0x%x\n", + le16_to_cpu(lr->client_id.seq_number)); + ntfs_log_info("client_id.client_index = 0x%x\n", + le16_to_cpu(lr->client_id.client_index)); + ntfs_log_info("record type = 0x%x\n", + (unsigned int)le32_to_cpu(lr->record_type)); + ntfs_log_info("transaction_id = 0x%x\n", + (unsigned int)le32_to_cpu(lr->transaction_id)); + ntfs_log_info("flags = 0x%x:", lr->flags); + if (!lr->flags) + ntfs_log_info(" NONE\n"); + else { + int _b = 0; + + if (lr->flags & LOG_RECORD_MULTI_PAGE) { + ntfs_log_info(" LOG_RECORD_MULTI_PAGE"); + _b = 1; + } + if (lr->flags & ~LOG_RECORD_MULTI_PAGE) { + if (_b) + ntfs_log_info(" |"); + ntfs_log_info(" Unknown flags"); + } + ntfs_log_info("\n"); + } + ntfs_log_info("redo_operation = 0x%x\n", le16_to_cpu(lr->redo_operation)); + ntfs_log_info("undo_operation = 0x%x\n", le16_to_cpu(lr->undo_operation)); + ntfs_log_info("redo_offset = 0x%x\n", le16_to_cpu(lr->redo_offset)); + ntfs_log_info("redo_length = 0x%x\n", le16_to_cpu(lr->redo_length)); + ntfs_log_info("undo_offset = 0x%x\n", le16_to_cpu(lr->undo_offset)); + ntfs_log_info("undo_length = 0x%x\n", le16_to_cpu(lr->undo_length)); + ntfs_log_info("target_attribute = 0x%x\n", le16_to_cpu(lr->target_attribute)); + ntfs_log_info("lcns_to_follow = 0x%x\n", le16_to_cpu(lr->lcns_to_follow)); + ntfs_log_info("record_offset = 0x%x\n", le16_to_cpu(lr->record_offset)); + ntfs_log_info("attribute_offset = 0x%x\n", le16_to_cpu(lr->attribute_offset)); + ntfs_log_info("target_vcn = 0x%llx\n", + (unsigned long long)sle64_to_cpu(lr->target_vcn)); + if (le16_to_cpu(lr->lcns_to_follow) > 0) + ntfs_log_info("Array of lcns:\n"); + for (i = 0; i < le16_to_cpu(lr->lcns_to_follow); i++) + ntfs_log_info("lcn_list[%u].lcn = 0x%llx\n", i, (unsigned long long) + sle64_to_cpu(lr->lcn_list[i].lcn)); +} + +/** + * dump_log_records() + */ +static void dump_log_records(RECORD_PAGE_HEADER *rcrd, u8 *buf, + int buf_size, unsigned int page_size) +{ + LOG_RECORD *lr; + int pass = 0; + int client; + + /* Reuse pass for log area. */ +rcrd_pass_loc: + rcrd = (RECORD_PAGE_HEADER*)((u8*)rcrd + page_size); + if ((u8*)rcrd + page_size > buf + buf_size) + return; + ntfs_log_info("\nLog record page number %i", pass); + if (!ntfs_is_rcrd_record(rcrd->magic) && + !ntfs_is_chkd_record(rcrd->magic)) { + unsigned int i; + for (i = 0; i < page_size; i++) + if (((u8*)rcrd)[i] != (u8)-1) + break; + if (i < page_size) + puts(" is corrupt (magic is not RCRD or CHKD)."); + else + puts(" is empty."); + pass++; + goto rcrd_pass_loc; + } else + puts(":"); + /* Dump log record page */ + ntfs_log_info("magic = %s\n", ntfs_is_rcrd_record(rcrd->magic) ? "RCRD" : + "CHKD"); +// TODO: I am here... (AIA) + ntfs_log_info("copy.last_lsn/file_offset = 0x%llx\n", (unsigned long long) + le64_to_cpu(rcrd->copy.last_lsn)); + ntfs_log_info("flags = 0x%x\n", (unsigned int)le32_to_cpu(rcrd->flags)); + ntfs_log_info("page count = %i\n", le16_to_cpu(rcrd->page_count)); + ntfs_log_info("page position = %i\n", le16_to_cpu(rcrd->page_position)); + ntfs_log_info("header.next_record_offset = 0x%llx\n", (unsigned long long) + le64_to_cpu(rcrd->header.packed.next_record_offset)); + ntfs_log_info("header.last_end_lsn = 0x%llx\n", (unsigned long long) + le64_to_cpu(rcrd->header.packed.last_end_lsn)); + /* + * Where does the 0x40 come from? Is it just usa_offset + + * usa_client * 2 + 7 & ~7 or is it derived from somewhere? + */ + lr = (LOG_RECORD*)((u8*)rcrd + 0x40); + client = 0; + do { + ntfs_log_info("\nLog record %i:\n", client); + dump_log_record(lr); + client++; + lr = (LOG_RECORD*)((u8*)lr + 0x70); + } while (((u8*)lr + 0x70 <= (u8*)rcrd + + le64_to_cpu(rcrd->header.packed.next_record_offset))); + + pass++; + goto rcrd_pass_loc; +} + +/** + * main - + */ +int main(int argc, char **argv) +{ + RESTART_PAGE_HEADER *rstr; + RECORD_PAGE_HEADER *rcrd; + unsigned int page_size; + int buf_size, br, err; + logfile_file logfile; + u8 *buf; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + ntfs_log_info("\n"); + if (argc < 2 || argc > 3) + /* print usage and exit */ + usage(argv[0]); + /* + * If one argument, it is a device containing an NTFS volume which we + * need to mount and read the $LogFile from so we can dump its + * contents. + * + * If two arguments the first one must be "-f" and the second one is + * the path and name of the $LogFile (or copy thereof) which we need to + * read and dump the contents of. + */ + + if (argc == 2) { + logfile_open(TRUE, argv[1], &logfile); + } else /* if (argc == 3) */ { + if (strncmp(argv[1], "-f", strlen("-f"))) + usage(argv[0]); + + logfile_open(FALSE, argv[2], &logfile); + } + + buf_size = 64 * 1024 * 1024; + + if (logfile.data_size <= buf_size) + buf_size = logfile.data_size; + else + ntfs_log_error("Warning: $LogFile is too big. " + "Only analysing the first 64MiB.\n"); + + /* For simplicity we read all of $LogFile/$DATA into memory. */ + buf = malloc(buf_size); + if (!buf) { + ntfs_log_perror("Failed to allocate buffer for file data"); + logfile_close(&logfile); + exit(1); + } + + br = logfile_pread(&logfile, 0, buf_size, buf); + err = errno; + logfile_close(&logfile); + if (br != buf_size) { + log_err_exit(buf, "Failed to read $LogFile/$DATA: %s\n", + br < 0 ? strerror(err) : "Partial read."); + } + + /* + * We now have the entirety of the journal ($LogFile/$DATA or argv[2]) + * in the memory buffer buf and this has a size of buf_size. Note we + * apply a size capping at 64MiB, so if the journal is any bigger we + * only have the first 64MiB. This should not be a problem as I have + * never seen such a large $LogFile. Usually it is only a few MiB in + * size. + */ + rstr = (RESTART_PAGE_HEADER*)buf; + + /* Check for presence of restart area signature. */ + if (!ntfs_is_rstr_record(rstr->magic) && + !ntfs_is_chkd_record(rstr->magic)) { + s8 *pos = (s8*)buf; + s8 *end = pos + buf_size; + while (pos < end && *pos == -1) + pos++; + if (pos != end) + log_err_exit(buf, "$LogFile contents are corrupt " + "(magic RSTR is missing). Cannot " + "handle this yet.\n"); + /* All bytes are -1. */ + free(buf); + puts("$LogFile is not initialized."); + return 0; + } + + /* + * First, verify the restart page header for consistency. + */ + restart_header_sanity(rstr, buf); + page_size = le32_to_cpu(rstr->log_page_size); + + /* + * Second, verify the restart area itself. + */ + // TODO: Implement this. + ntfs_log_error("Warning: Sanity checking of restart area not implemented " + "yet.\n"); + /* + * Third and last, verify the array of log client records. + */ + // TODO: Implement this. + ntfs_log_error("Warning: Sanity checking of array of log client records not " + "implemented yet.\n"); + + /* + * Dump the restart headers & areas. + */ + rcrd = (RECORD_PAGE_HEADER*)dump_restart_areas(rstr, buf, page_size); + ntfs_log_info("\n\nFinished with restart pages. " + "Beginning with log pages.\n"); + + /* + * Dump the log areas. + */ + dump_log_records(rcrd, buf, buf_size, page_size); + + free(buf); + return 0; +} + diff --git a/ntfsprogs/ntfsfallocate.8 b/ntfsprogs/ntfsfallocate.8 new file mode 100755 index 0000000000000000000000000000000000000000..af971de486b110cec2be05dff0fa4e845cc88969 --- /dev/null +++ b/ntfsprogs/ntfsfallocate.8 @@ -0,0 +1,137 @@ +.\" Copyright (c) 2014 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSFALLOCATE 8 "June 2014" "ntfs-3g 2015.3.14" +.SH NAME +ntfsfallocate \- preallocate space to a file on an NTFS volume +.SH SYNOPSIS +\fBntfsfallocate\fR [\fIoptions\fR] -l \fIlength\fR \fIdevice\fR \fIfile\fR \fI[attr-type\fR [\fIattr-name\fR]] +.SH DESCRIPTION +.B ntfsfallocate +preallocates space for any attribute of a file or directory, thus reserving +space before actual contents is written. This is similar to fallocate(1). +.SH OPTIONS +Below is a summary of all the options that +.B ntfsfallocate +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not using a mounted volume. +Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-l\fR, \fB\-\-length\fR LENGTH +This is a mandatory option to specify the number of bytes to preallocate. +It will be rounded up to a multiple of the cluster size. +A suffix of K, M, G, T, P or E may be appended to +mean a multiplicative factor of a power of 1000. Similarly a suffix of +Ki, Mi, Gi, Ti, Pi or Ei may be appended to mean a multiplicative factor +of a power of 1024. +.TP +\fB\-n\fR, \fB\-\-no-size-change\fR +Do not change the apparent size of the file. The space allocated beyond +the apparent size is not zeroed, but subsequent writing beyond the apparent +end of file will force zeroing the inner allocated space as it cannot be +considered as a hole any more, and this may take significant time. +.TP +\fB\-N\fR, \fB\-\-no-action\fR +Simulate the allocation without actually write to device. +.TP +\fB\-o\fR, \fB\-\-offset\fR OFFSET +Specify the offset in the file where preallocation starts. By default, +the preallocation is counted from the beginning of the file. Space already +allocated in the area defined by offset and length is preserved. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license of +.BR ntfsfallocate . +.TP +\fBattr-type\fR +Define a particular attribute type to be preallocated (advanced use only). +By default, the unnamed $DATA attribute (the contents of a plain file) will +be preallocated. The attribute has to be specified by a number in decimal +or hexadecimal : +.TS +box; +lB lB lB +l l l. +Hex Decimal Name +0x10 16 "$STANDARD_INFORMATION" +0x20 32 "$ATTRIBUTE_LIST" +0x30 48 "$FILE_NAME" +0x40 64 "$OBJECT_ID" +0x50 80 "$SECURITY_DESCRIPTOR" +0x60 96 "$VOLUME_NAME" +0x70 112 "$VOLUME_INFORMATION" +0x80 128 "$DATA" +0x90 144 "$INDEX_ROOT" +0xA0 160 "$INDEX_ALLOCATION" +0xB0 176 "$BITMAP" +0xC0 192 "$REPARSE_POINT" +0xD0 208 "$EA_INFORMATION" +0xE0 224 "$EA" +0xF0 240 "$PROPERTY_SET" +0x100 256 "$LOGGED_UTILITY_STREAM" +.TE +.sp +.TP +\fBattr-name\fR +Define the name of the particular attribute type to be preallocated +(advanced use only). +.SH EXAMPLES +Preallocate 100MB to the file database.db located in the Data directory +which is at the root of an NTFS file system. +.RS +.sp +.B ntfsfallocate -l 100M /dev/sda1 Data/database.db +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfsfallocate , +however it can lead to configurations not supported by Windows +and Windows may crash (BSOD) when writing to preallocated clusters +which were not written to earlier. Files with preallocated space should +be fully be written to before they are updated by Windows. +.P +If you find a bug in \fBntfsfallocate\fR proper, please send an email +describing the problem to the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfsfallocate +was written by Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsfallocate +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfs-3g (8), +.BR ntfstruncate (8), +.BR ntfsprogs (8), +.BR fallocate (1) diff --git a/ntfsprogs/ntfsfallocate.8.in b/ntfsprogs/ntfsfallocate.8.in new file mode 100755 index 0000000000000000000000000000000000000000..04cc9ea97764d2cdfe6532ad076084ba08c7c762 --- /dev/null +++ b/ntfsprogs/ntfsfallocate.8.in @@ -0,0 +1,137 @@ +.\" Copyright (c) 2014 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSFALLOCATE 8 "June 2014" "ntfs-3g @VERSION@" +.SH NAME +ntfsfallocate \- preallocate space to a file on an NTFS volume +.SH SYNOPSIS +\fBntfsfallocate\fR [\fIoptions\fR] -l \fIlength\fR \fIdevice\fR \fIfile\fR \fI[attr-type\fR [\fIattr-name\fR]] +.SH DESCRIPTION +.B ntfsfallocate +preallocates space for any attribute of a file or directory, thus reserving +space before actual contents is written. This is similar to fallocate(1). +.SH OPTIONS +Below is a summary of all the options that +.B ntfsfallocate +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not using a mounted volume. +Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-l\fR, \fB\-\-length\fR LENGTH +This is a mandatory option to specify the number of bytes to preallocate. +It will be rounded up to a multiple of the cluster size. +A suffix of K, M, G, T, P or E may be appended to +mean a multiplicative factor of a power of 1000. Similarly a suffix of +Ki, Mi, Gi, Ti, Pi or Ei may be appended to mean a multiplicative factor +of a power of 1024. +.TP +\fB\-n\fR, \fB\-\-no-size-change\fR +Do not change the apparent size of the file. The space allocated beyond +the apparent size is not zeroed, but subsequent writing beyond the apparent +end of file will force zeroing the inner allocated space as it cannot be +considered as a hole any more, and this may take significant time. +.TP +\fB\-N\fR, \fB\-\-no-action\fR +Simulate the allocation without actually write to device. +.TP +\fB\-o\fR, \fB\-\-offset\fR OFFSET +Specify the offset in the file where preallocation starts. By default, +the preallocation is counted from the beginning of the file. Space already +allocated in the area defined by offset and length is preserved. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license of +.BR ntfsfallocate . +.TP +\fBattr-type\fR +Define a particular attribute type to be preallocated (advanced use only). +By default, the unnamed $DATA attribute (the contents of a plain file) will +be preallocated. The attribute has to be specified by a number in decimal +or hexadecimal : +.TS +box; +lB lB lB +l l l. +Hex Decimal Name +0x10 16 "$STANDARD_INFORMATION" +0x20 32 "$ATTRIBUTE_LIST" +0x30 48 "$FILE_NAME" +0x40 64 "$OBJECT_ID" +0x50 80 "$SECURITY_DESCRIPTOR" +0x60 96 "$VOLUME_NAME" +0x70 112 "$VOLUME_INFORMATION" +0x80 128 "$DATA" +0x90 144 "$INDEX_ROOT" +0xA0 160 "$INDEX_ALLOCATION" +0xB0 176 "$BITMAP" +0xC0 192 "$REPARSE_POINT" +0xD0 208 "$EA_INFORMATION" +0xE0 224 "$EA" +0xF0 240 "$PROPERTY_SET" +0x100 256 "$LOGGED_UTILITY_STREAM" +.TE +.sp +.TP +\fBattr-name\fR +Define the name of the particular attribute type to be preallocated +(advanced use only). +.SH EXAMPLES +Preallocate 100MB to the file database.db located in the Data directory +which is at the root of an NTFS file system. +.RS +.sp +.B ntfsfallocate -l 100M /dev/sda1 Data/database.db +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfsfallocate , +however it can lead to configurations not supported by Windows +and Windows may crash (BSOD) when writing to preallocated clusters +which were not written to earlier. Files with preallocated space should +be fully be written to before they are updated by Windows. +.P +If you find a bug in \fBntfsfallocate\fR proper, please send an email +describing the problem to the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfsfallocate +was written by Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsfallocate +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfs-3g (8), +.BR ntfstruncate (8), +.BR ntfsprogs (8), +.BR fallocate (1) diff --git a/ntfsprogs/ntfsfallocate.c b/ntfsprogs/ntfsfallocate.c new file mode 100755 index 0000000000000000000000000000000000000000..1b243ae6987fbbc08c50d6a5c0d26cf01e4152db --- /dev/null +++ b/ntfsprogs/ntfsfallocate.c @@ -0,0 +1,903 @@ +/** + * ntfsfallocate + * + * Copyright (c) 2013-2014 Jean-Pierre Andre + * + * This utility will allocate clusters to a specified attribute belonging + * to a specified file or directory, to a specified length. + * + * WARNING : this can lead to configurations not supported by Windows + * and Windows may crash (BSOD) when writing to preallocated clusters + * which were not written to. + * + * 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 (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else +extern char *optarg; +extern int optind; +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#ifndef LLONG_MAX +#define LLONG_MAX 9223372036854775807LL +#endif + +#include "types.h" +#include "attrib.h" +#include "inode.h" +#include "layout.h" +#include "volume.h" +#include "logging.h" +#include "runlist.h" +#include "dir.h" +#include "bitmap.h" +#include "lcnalloc.h" +#include "utils.h" +#include "misc.h" + +const char *EXEC_NAME = "ntfsfallocate"; + +char *dev_name; +const char *file_name; +le32 attr_type; +ntfschar *attr_name = NULL; +u32 attr_name_len; +s64 opt_alloc_offs; +s64 opt_alloc_len; + +ATTR_DEF *attr_defs; + +static struct { + /* -h, print usage and exit. */ + int no_action; /* do not write to device, only display + what would be done. */ + int no_size_change; /* -n, do not change the apparent size */ + int quiet; /* -q, quiet execution. */ + int verbose; /* -v, verbose execution, given twice, really + verbose execution (debug mode). */ + int force; /* -f, force allocation. */ + /* -V, print version and exit. */ +} opts; + +static const struct option lopt[] = { + { "offset", required_argument, NULL, 'o' }, + { "length", required_argument, NULL, 'l' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "no-action", no_argument, NULL, 'N' }, + { "no-size-change", no_argument, NULL, 'n' }, + { "quiet", no_argument, NULL, 'q' }, + { "version", no_argument, NULL, 'V' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } +}; + +/** + * err_exit - error output and terminate; ignores quiet (-q) + * + * DO NOT USE when allocations are not in initial state + */ +__attribute__((noreturn)) +__attribute__((format(printf, 2, 3))) +static void err_exit(ntfs_volume *vol, const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "Aborting...\n"); + if (vol && ntfs_umount(vol, 0)) + fprintf(stderr, "Warning: Could not umount %s\n", dev_name); + exit(1); +} + +/** + * copyright - print copyright statements + */ +static void copyright(void) +{ + fprintf(stderr, "Copyright (c) 2013-2014 Jean-Pierre Andre\n" + "Allocate clusters to a specified attribute of " + "a specified file.\n"); +} + +/** + * license - print license statement + */ +static void license(void) +{ + fprintf(stderr, "%s", ntfs_gpl); +} + +/** + * usage - print a list of the parameters to the program + */ +__attribute__((noreturn)) +static void usage(int ret) +{ + copyright(); + fprintf(stderr, "Usage: %s [options] -l length device file [attr-type " + "[attr-name]]\n" + " If attr-type is not specified, 0x80 (i.e. $DATA) " + "is assumed.\n" + " If attr-name is not specified, an unnamed " + "attribute is assumed.\n" + " -f Force execution despite errors\n" + " -n Do not change the apparent size of file\n" + " -l length Allocate length bytes\n" + " -o offset Start allocating at offset\n" + " -v Verbose execution\n" + " -vv Very verbose execution\n" + " -V Display version information\n" + " -h Display this help\n", EXEC_NAME); + fprintf(stderr, "%s%s", ntfs_bugs, ntfs_home); + exit(ret); +} + +/** + * err_exit - error output, display usage and exit + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static void err_usage(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + usage(1); +} + +/* + * Get a value option with a possible multiple suffix + */ + +static s64 option_value(const char *arg) +{ + s64 ll; + char *s; + s64 fact; + int count; + BOOL err; + + err = FALSE; + ll = strtoll(arg, &s, 0); + if ((ll >= LLONG_MAX) && (errno == ERANGE)) + err_exit((ntfs_volume*)NULL, "Too big value : %s\n",arg); + if (*s) { + count = 0; + switch (*s++) { + case 'E' : count++; + case 'P' : count++; + case 'T' : count++; + case 'G' : count++; + case 'M' : count++; + case 'K' : count++; + switch (*s++) { + case 'i' : + fact = 1024; + if (*s++ != 'B') + err = TRUE; + break; + case 'B' : + fact = 1000; + break; + case '\0' : + fact = 1024; + s--; + break; + default : + err = TRUE; + fact = 1; + break; + } + if (*s) + err = TRUE; + break; + default : + err = TRUE; + break; + } + if (err) + err_exit((ntfs_volume*)NULL, + "Invalid suffix in : %s\n",arg); + else + while (count-- > 0) { + if (ll > LLONG_MAX/1024) + err_exit((ntfs_volume*)NULL, + "Too big value : %s\n",arg); + ll *= fact; + } + } + return (ll); +} + + +/** + * parse_options + */ +static void parse_options(int argc, char *argv[]) +{ + long long ll; + char *s, *s2; + int c; + + opt_alloc_len = 0; + opt_alloc_offs = 0; + if (argc && *argv) + EXEC_NAME = *argv; + fprintf(stderr, "%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + while ((c = getopt_long(argc, argv, "fh?no:qvVl:", lopt, NULL)) != EOF) { + switch (c) { + case 'f': + opts.force = 1; + break; + case 'n': + opts.no_size_change = 1; + break; + case 'N': /* Not proposed as a short option */ + opts.no_action = 1; + break; + case 'q': + opts.quiet = 1; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + /* Version number already printed */ + license(); + exit(0); + case 'l': + ll = option_value(argv[optind - 1]); + if ((ll <= 0) + || (ll >= LLONG_MAX && errno == ERANGE)) + err_usage("Invalid length : %s\n", + argv[optind - 1]); + opt_alloc_len = ll; + break; + case 'o': + ll = option_value(argv[optind - 1]); + if ((ll < 0) + || (ll >= LLONG_MAX && errno == ERANGE)) + err_usage("Invalid offset : %s\n", + argv[optind - 1]); + opt_alloc_offs = ll; + break; + case 'h': + usage(0); + case '?': + default: + usage(1); + } + } + if (!opt_alloc_len) { + err_usage("Missing allocation length\n"); + } + + ntfs_log_verbose("length = %lli = 0x%llx\n", + (long long)opt_alloc_len, (long long)opt_alloc_len); + ntfs_log_verbose("offset = %lli = 0x%llx\n", + (long long)opt_alloc_offs, (long long)opt_alloc_offs); + + if (optind == argc) + usage(1); + + if (opts.verbose > 1) + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | + NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET); + + /* Get the device. */ + dev_name = argv[optind++]; + ntfs_log_verbose("device name = %s\n", dev_name); + + if (optind == argc) + usage(1); + + /* Get the file name. */ + file_name = argv[optind++]; + ntfs_log_verbose("file name = \"%s\"\n", file_name); + + /* Get the attribute type, if specified. */ + if (optind == argc) { + attr_type = AT_DATA; + attr_name = AT_UNNAMED; + attr_name_len = 0; + } else { + unsigned long ul; + + s = argv[optind++]; + ul = strtoul(s, &s2, 0); + if (*s2 || !ul || (ul >= ULONG_MAX && errno == ERANGE)) + err_usage("Invalid attribute type %s: %s\n", s, + strerror(errno)); + attr_type = cpu_to_le32(ul); + + /* Get the attribute name, if specified. */ + if (optind != argc) { + s = argv[optind++]; + /* Convert the string to little endian Unicode. */ + attr_name_len = ntfs_mbstoucs(s, &attr_name); + if ((int)attr_name_len < 0) + err_usage("Invalid attribute name " + "\"%s\": %s\n", + s, strerror(errno)); + + /* Keep hold of the original string. */ + s2 = s; + + s = argv[optind++]; + if (optind != argc) + usage(1); + } else { + attr_name = AT_UNNAMED; + attr_name_len = 0; + } + } + ntfs_log_verbose("attribute type = 0x%lx\n", + (unsigned long)le32_to_cpu(attr_type)); + if (attr_name == AT_UNNAMED) + ntfs_log_verbose("attribute name = \"\" (UNNAMED)\n"); + else + ntfs_log_verbose("attribute name = \"%s\" (length %u " + "Unicode characters)\n", s2, + (unsigned int)attr_name_len); +} + +/* + * Save the initial runlist, to be restored on error + */ + +static runlist_element *ntfs_save_rl(runlist_element *rl) +{ + runlist_element *save; + int n; + + n = 0; + save = (runlist_element*)NULL; + if (rl) { + while (rl[n].length) + n++; + save = (runlist_element*)malloc((n + 1)*sizeof(runlist_element)); + if (save) { + memcpy(save, rl, (n + 1)*sizeof(runlist_element)); + } + } + return (save); +} + +/* + * Free the common part of two runs + */ + +static void free_common(ntfs_volume *vol, runlist_element *brl, s64 blth, + runlist_element *grl, s64 glth) +{ + VCN begin_common; + VCN end_common; + + begin_common = max(grl->vcn, brl->vcn); + end_common = min(grl->vcn + glth, brl->vcn + blth); + if (end_common > begin_common) { + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, + brl->lcn + begin_common - brl->vcn, + end_common - begin_common)) + ntfs_log_error("Failed to free %lld clusters " + "from 0x%llx\n", + (long long)end_common - begin_common, + (long long)(brl->lcn + begin_common + - brl->vcn)); + } +} + +/* + * Restore the cluster allocations to initial state + * + * If a new error occurs, only output a message + */ + +static void ntfs_restore_rl(ntfs_attr *na, runlist_element *oldrl) +{ + runlist_element *brl; /* Pointer to bad runlist */ + runlist_element *grl; /* Pointer to good runlist */ + ntfs_volume *vol; + + vol = na->ni->vol; + /* Examine allocated entries from the bad runlist */ + for (brl=na->rl; brl->length; brl++) { + if (brl->lcn != LCN_HOLE) { +// TODO improve by examining both list in parallel + /* Find the holes in the good runlist which overlap */ + for (grl=oldrl; grl->length + && (grl->vcn<=(brl->vcn+brl->length)); grl++) { + if (grl->lcn == LCN_HOLE) { + free_common(vol, brl, brl->length, grl, + grl->length); + } + } + /* Free allocations beyond the end of good runlist */ + if (grl && !grl->length + && ((brl->vcn + brl->length) > grl->vcn)) { + free_common(vol, brl, brl->length, grl, + brl->vcn + brl->length - grl->vcn); + } + } + } + free(na->rl); + na->rl = oldrl; + if (ntfs_attr_update_mapping_pairs(na, 0)) { + ntfs_log_error("Failed to restore the original runlist\n"); + } +} + +/* + * Zero newly allocated runs up to initialized_size + */ + +static int ntfs_inner_zero(ntfs_attr *na, runlist_element *rl) +{ + ntfs_volume *vol; + char *buf; + runlist_element *zrl; + s64 cofs; + s64 pos; + s64 zeroed; + int err; + + err = 0; + vol = na->ni->vol; + buf = (char*)malloc(vol->cluster_size); + if (buf) { + memset(buf, 0, vol->cluster_size); + zrl = rl; + pos = zrl->vcn << vol->cluster_size_bits; + while (zrl->length + && !err + && (pos < na->initialized_size)) { + for (cofs=0; cofs<zrl->length && !err; cofs++) { + zeroed = ntfs_pwrite(vol->dev, + (rl->lcn + cofs) + << vol->cluster_size_bits, + vol->cluster_size, buf); + if (zeroed != vol->cluster_size) { + ntfs_log_error("Failed to zero at " + "offset %lld\n", + (long long)pos); + errno = EIO; + err = -1; + } + pos += vol->cluster_size; + } + zrl++; + pos = zrl->vcn << vol->cluster_size_bits; + } + free(buf); + } else { + ntfs_log_error("Failed to allocate memory\n"); + errno = ENOSPC; + err = -1; + } + return (err); +} + +/* + * Merge newly allocated runs into runlist + */ + +static int ntfs_merge_allocation(ntfs_attr *na, runlist_element *rl, + s64 size) +{ + ntfs_volume *vol; + int err; + + err = 0; + vol = na->ni->vol; + /* Newly allocated clusters before initialized size need be zeroed */ + if ((rl->vcn << vol->cluster_size_bits) < na->initialized_size) { + err = ntfs_inner_zero(na, rl); + } + if (!err) { + if (na->data_flags & ATTR_IS_SPARSE) { + na->compressed_size += size; + if (na->compressed_size >= na->allocated_size) { + na->data_flags &= ~ATTR_IS_SPARSE; + if (na->compressed_size > na->allocated_size) { + ntfs_log_error("File size error : " + "apparent %lld, " + "compressed %lld > " + "allocated %lld", + (long long)na->data_size, + (long long)na->compressed_size, + (long long)na->allocated_size); + errno = EIO; + err = -1; + } + } + } + } + if (!err) { + rl = ntfs_runlists_merge(na->rl, rl); + if (!rl) { + ntfs_log_error("Failed to merge the new allocation\n"); + err = -1; + } else { + na->rl = rl; + /* Update the runlist */ + if (ntfs_attr_update_mapping_pairs(na, 0)) { + ntfs_log_error( + "Failed to update the runlist\n"); + err = -1; + } + } + } + return (err); +} + +static int ntfs_inner_allocation(ntfs_attr *na, s64 alloc_offs, s64 alloc_len) +{ + ntfs_volume *vol; + runlist_element *rl; + runlist_element *prl; + runlist_element *rlc; + VCN from_vcn; + VCN end_vcn; + LCN lcn_seek_from; + VCN from_hole; + VCN end_hole; + s64 need; + int err; + BOOL done; + + err = 0; + vol = na->ni->vol; + /* Find holes which overlap the requested allocation */ + from_vcn = alloc_offs >> vol->cluster_size_bits; + end_vcn = (alloc_offs + alloc_len + vol->cluster_size - 1) + >> vol->cluster_size_bits; + do { + done = FALSE; + rl = na->rl; + while (rl->length + && ((rl->lcn >= 0) + || ((rl->vcn + rl->length) <= from_vcn) + || (rl->vcn >= end_vcn))) + rl++; + if (!rl->length) + done = TRUE; + else { + from_hole = max(from_vcn, rl->vcn); + end_hole = min(end_vcn, rl->vcn + rl->length); + need = end_hole - from_hole; + lcn_seek_from = -1; + if (rl->vcn) { + /* Avoid fragmentation when possible */ + prl = rl; + if ((--prl)->lcn >= 0) { + lcn_seek_from = prl->lcn + + from_hole - prl->vcn; + } + } + if (need <= 0) { + ntfs_log_error("Wrong hole size %lld\n", + (long long)need); + errno = EIO; + err = -1; + } else { + rlc = ntfs_cluster_alloc(vol, from_hole, need, + lcn_seek_from, DATA_ZONE); + if (!rlc) + err = -1; + else + err = ntfs_merge_allocation(na, rlc, + need << vol->cluster_size_bits); + } + } + } while (!err && !done); + return (err); +} + +static int ntfs_full_allocation(ntfs_attr *na, ntfs_attr_search_ctx *ctx, + s64 alloc_offs, s64 alloc_len) +{ + ATTR_RECORD *attr; + ntfs_inode *ni; + s64 initialized_size; + s64 data_size; + int err; + + err = 0; + initialized_size = na->initialized_size; + data_size = na->data_size; + + if (na->allocated_size <= alloc_offs) { + /* + * Request is fully beyond what was already allocated : + * only need to expand the attribute + */ + err = ntfs_attr_truncate(na, alloc_offs); + if (!err) + err = ntfs_attr_truncate_solid(na, + alloc_offs + alloc_len); + } else { + /* + * Request overlaps what was already allocated : + * We may have to fill existing holes, and force zeroes + * into clusters which are visible. + */ + if ((alloc_offs + alloc_len) > na->allocated_size) + err = ntfs_attr_truncate(na, alloc_offs + alloc_len); + if (!err) + err = ntfs_inner_allocation(na, alloc_offs, alloc_len); + } + /* Set the sizes, even after an error, to keep consistency */ + na->initialized_size = initialized_size; + /* Restore the original apparent size if requested or error */ + if (err || opts.no_size_change + || ((alloc_offs + alloc_len) < data_size)) + na->data_size = data_size; + else { + /* + * "man 1 fallocate" does not define the new apparent size + * when size change is allowed (no --keep-size). + * Assuming the same as no FALLOC_FL_KEEP_SIZE in fallocate(2) : + * "the file size will be changed if offset + len is greater + * than the file size" +// TODO check the behavior of another file system + */ + na->data_size = alloc_offs + alloc_len; + } + + if (!err) { + /* Find the attribute, which may have been relocated for allocations */ + if (ntfs_attr_lookup(attr_type, attr_name, attr_name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + err = -1; + ntfs_log_error("Failed to locate the attribute\n"); + } else { + /* Feed the sizes into the attribute */ + attr = ctx->attr; + attr->data_size = cpu_to_le64(na->data_size); + attr->initialized_size + = cpu_to_le64(na->initialized_size); + attr->allocated_size + = cpu_to_le64(na->allocated_size); + if (na->data_flags & ATTR_IS_SPARSE) + attr->compressed_size + = cpu_to_le64(na->compressed_size); + /* Copy the unnamed data attribute sizes to inode */ + if ((attr_type == AT_DATA) && !attr_name_len) { + ni = na->ni; + ni->data_size = na->data_size; + if (na->data_flags & ATTR_IS_SPARSE) { + ni->allocated_size + = na->compressed_size; + ni->flags |= FILE_ATTR_SPARSE_FILE; + } else + ni->allocated_size + = na->allocated_size; + } + } + } + return (err); +} + + +/* + * Do the actual allocations + */ + +static int ntfs_fallocate(ntfs_inode *ni, s64 alloc_offs, s64 alloc_len) +{ + s64 allocated_size; + s64 data_size; + ntfs_attr_search_ctx *ctx; + ntfs_attr *na; + runlist_element *oldrl; + const char *errmess; + int save_errno; + int err; + + err = 0; + /* Open the specified attribute. */ + na = ntfs_attr_open(ni, attr_type, attr_name, attr_name_len); + if (!na) { + ntfs_log_perror("Failed to open attribute 0x%lx: ", + (unsigned long)le32_to_cpu(attr_type)); + err = -1; + } else { + errmess = (const char*)NULL; + if (na->data_flags & ATTR_IS_COMPRESSED) { + errmess= "Cannot fallocate a compressed file"; + } + + /* Locate the attribute record, needed for updating sizes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + errmess = "Failed to allocate a search context"; + } + if (errmess) { + ntfs_log_error("%s\n",errmess); + err = -1; + } else { + /* Get and save the initial allocations */ + allocated_size = na->allocated_size; + data_size = ni->data_size; + err = ntfs_attr_map_whole_runlist(na); + if (!err) { + oldrl = ntfs_save_rl(na->rl); + if (oldrl) { + err = ntfs_full_allocation(na, ctx, + alloc_offs, alloc_len); + if (err) { + save_errno = errno; + ni->allocated_size + = allocated_size; + ni->data_size = data_size; + ntfs_restore_rl(na, oldrl); + errno = save_errno; + } else { + free(oldrl); + /* Mark file name dirty, to update the sizes in directories */ + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); + } + } else + err = -1; + } + ntfs_attr_put_search_ctx(ctx); + } + /* Close the attribute. */ + ntfs_attr_close(na); + } + return (err); +} + +/** + * main + */ +int main(int argc, char **argv) +{ + unsigned long mnt_flags, ul; + int err; + ntfs_inode *ni; + ntfs_volume *vol; +#ifdef HAVE_WINDOWS_H + char *unix_name; +#endif + + vol = (ntfs_volume*)NULL; + ntfs_log_set_handler(ntfs_log_handler_outerr); + + /* Initialize opts to zero / required values. */ + memset(&opts, 0, sizeof(opts)); + + /* Parse command line options. */ + parse_options(argc, argv); + + utils_set_locale(); + + /* Make sure the file system is not mounted. */ + if (ntfs_check_if_mounted(dev_name, &mnt_flags)) + ntfs_log_perror("Failed to determine whether %s is mounted", + dev_name); + else if (mnt_flags & NTFS_MF_MOUNTED) { + ntfs_log_error("%s is mounted.\n", dev_name); + if (!opts.force) + err_exit((ntfs_volume*)NULL, "Refusing to run!\n"); + fprintf(stderr, "ntfsfallocate forced anyway. Hope /etc/mtab " + "is incorrect.\n"); + } + + /* Mount the device. */ + if (opts.no_action) { + ntfs_log_quiet("Running in READ-ONLY mode!\n"); + ul = NTFS_MNT_RDONLY; + } else + if (opts.force) + ul = NTFS_MNT_RECOVER; + else + ul = 0; + vol = ntfs_mount(dev_name, ul); + if (!vol) + err_exit(vol, "Failed to mount %s: %s\n", dev_name, + strerror(errno)); + + if ((vol->flags & VOLUME_IS_DIRTY) && !opts.force) + err_exit(vol, "Volume is dirty, please run chkdsk.\n"); + + if (ntfs_volume_get_free_space(vol)) + err_exit(vol, "Failed to get free clusters %s: %s\n", + dev_name, strerror(errno)); + + /* Open the specified inode. */ +#ifdef HAVE_WINDOWS_H + unix_name = (char*)malloc(strlen(file_name) + 1); + if (unix_name) { + int i; + for (i=0; file_name[i]; i++) + if (file_name[i] == '\\') + unix_name[i] = '/'; + else + unix_name[i] = file_name[i]; + unix_name[i] = 0; + ni = ntfs_pathname_to_inode(vol, NULL, unix_name); + free(unix_name); + } else + ni = (ntfs_inode*)NULL; +#else + ni = ntfs_pathname_to_inode(vol, NULL, file_name); +#endif + if (!ni) + err_exit(vol, "Failed to open file \"%s\": %s\n", file_name, + strerror(errno)); + if (!opts.no_action) + err = ntfs_fallocate(ni, opt_alloc_offs, opt_alloc_len); + + /* Close the inode. */ + if (ntfs_inode_close(ni)) { + err = -1; + err_exit(vol, "Failed to close inode \"%s\" : %s\n", file_name, + strerror(errno)); + } + + /* Unmount the volume. */ + err = ntfs_umount(vol, 0); + vol = (ntfs_volume*)NULL; + if (err) + ntfs_log_perror("Warning: Failed to umount %s", dev_name); + + /* Free the attribute name if it exists. */ + if (attr_name_len) + ntfs_ucsfree(attr_name); + + ntfs_log_quiet("ntfsfallocate completed successfully. Have a nice day.\n"); + return 0; +} diff --git a/ntfsprogs/ntfsfix.8 b/ntfsprogs/ntfsfix.8 new file mode 100755 index 0000000000000000000000000000000000000000..e3d31f479dcfb6b5a6e9497ae8b40780c2249a53 --- /dev/null +++ b/ntfsprogs/ntfsfix.8 @@ -0,0 +1,82 @@ +.\" Copyright (c) 2005-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2005 Richard Russon. +.\" Copyright (c) 2011-2013 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSFIX 8 "January 2013" "ntfs-3g 2015.3.14" +.SH NAME +ntfsfix \- fix common errors and force Windows to check NTFS +.SH SYNOPSIS +.B ntfsfix +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfsfix +is a utility that fixes some common NTFS problems. +.B ntfsfix +is +.B NOT +a Linux version of chkdsk. It only repairs some fundamental NTFS +inconsistencies, resets the NTFS journal file and schedules an NTFS consistency +check for the first boot into Windows. +.sp +You may run +.B ntfsfix +on an NTFS volume if you think it was damaged by Windows or some other way +and it cannot be mounted. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsfix +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-b\fR, \fB\-\-clear\-bad\-sectors\fR +Clear the list of bad sectors. This is useful after cloning an old disk +with bad sectors to a new disk. +.TP +\fB\-d\fR, \fB\-\-clear\-dirty\fR +Clear the volume dirty flag if the volume can be fixed and mounted. +If the option is not present or the volume cannot be fixed, the dirty +volume flag is set to request a volume checking at next mount. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Do not write anything, just show what would have been done. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license +.SH BUGS +There are no known problems with +.BR ntfsfix . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfsfix +was written by Anton Altaparmakov, with contributions from Szabolcs Szakacsits. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsfix +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR mkntfs (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsfix.8.in b/ntfsprogs/ntfsfix.8.in new file mode 100755 index 0000000000000000000000000000000000000000..51010001c96f78943d1d21c588f0368f94be05fe --- /dev/null +++ b/ntfsprogs/ntfsfix.8.in @@ -0,0 +1,82 @@ +.\" Copyright (c) 2005-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2005 Richard Russon. +.\" Copyright (c) 2011-2013 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSFIX 8 "January 2013" "ntfs-3g @VERSION@" +.SH NAME +ntfsfix \- fix common errors and force Windows to check NTFS +.SH SYNOPSIS +.B ntfsfix +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfsfix +is a utility that fixes some common NTFS problems. +.B ntfsfix +is +.B NOT +a Linux version of chkdsk. It only repairs some fundamental NTFS +inconsistencies, resets the NTFS journal file and schedules an NTFS consistency +check for the first boot into Windows. +.sp +You may run +.B ntfsfix +on an NTFS volume if you think it was damaged by Windows or some other way +and it cannot be mounted. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsfix +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-b\fR, \fB\-\-clear\-bad\-sectors\fR +Clear the list of bad sectors. This is useful after cloning an old disk +with bad sectors to a new disk. +.TP +\fB\-d\fR, \fB\-\-clear\-dirty\fR +Clear the volume dirty flag if the volume can be fixed and mounted. +If the option is not present or the volume cannot be fixed, the dirty +volume flag is set to request a volume checking at next mount. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Do not write anything, just show what would have been done. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license +.SH BUGS +There are no known problems with +.BR ntfsfix . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfsfix +was written by Anton Altaparmakov, with contributions from Szabolcs Szakacsits. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsfix +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR mkntfs (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c new file mode 100755 index 0000000000000000000000000000000000000000..59b8a98fbd8bfd23475ecb7c2dd7e7e62ba94096 --- /dev/null +++ b/ntfsprogs/ntfsfix.c @@ -0,0 +1,1655 @@ +/** + * ntfsfix - Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2006 Anton Altaparmakov + * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2007 Yura Pakhuchiy + * Copyright (c) 2011-2014 Jean-Pierre Andre + * + * This utility fixes some common NTFS problems, resets the NTFS journal file + * and schedules an NTFS consistency check for the first boot into Windows. + * + * Anton Altaparmakov <aia21@cantab.net> + * + * 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 (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * WARNING: This program might not work on architectures which do not allow + * unaligned access. For those, the program would need to start using + * get/put_unaligned macros (#include <asm/unaligned.h>), but not doing it yet, + * since NTFS really mostly applies to ia32 only, which does allow unaligned + * accesses. We might not actually have a problem though, since the structs are + * defined as being packed so that might be enough for gcc to insert the + * correct code. + * + * If anyone using a non-little endian and/or an aligned access only CPU tries + * this program please let me know whether it works or not! + * + * Anton Altaparmakov <aia21@cantab.net> + */ + +#include "config.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +#include "types.h" +#include "attrib.h" +#include "volume.h" +#include "bootsect.h" +#include "mft.h" +#include "device.h" +#include "logfile.h" +#include "runlist.h" +#include "mst.h" +#include "utils.h" +/* #include "version.h" */ +#include "logging.h" +#include "misc.h" + +#ifdef NO_NTFS_DEVICE_DEFAULT_IO_OPS +# error "No default device io operations! Cannot build ntfsfix. \ +You need to run ./configure without the --disable-default-device-io-ops \ +switch if you want to be able to build the NTFS utilities." +#endif + +static const char *EXEC_NAME = "ntfsfix"; +static const char OK[] = "OK\n"; +static const char FAILED[] = "FAILED\n"; +static const char FOUND[] = "FOUND\n"; + +#define DEFAULT_SECTOR_SIZE 512 + +static struct { + char *volume; + BOOL no_action; + BOOL clear_bad_sectors; + BOOL clear_dirty; +} opt; + +/* + * Definitions for fixing the self-located MFT bug + */ + +#define SELFLOC_LIMIT 16 + +struct MFT_SELF_LOCATED { + ntfs_volume *vol; + MFT_RECORD *mft0; + MFT_RECORD *mft1; + MFT_RECORD *mft2; + ATTR_LIST_ENTRY *attrlist; + ATTR_LIST_ENTRY *attrlist_to_ref1; + MFT_REF mft_ref0; + MFT_REF mft_ref1; + LCN attrlist_lcn; + BOOL attrlist_resident; +} ; + +/** + * usage + */ +__attribute__((noreturn)) +static void usage(int ret) +{ + ntfs_log_info("%s v%s (libntfs-3g)\n" + "\n" + "Usage: %s [options] device\n" + " Attempt to fix an NTFS partition.\n" + "\n" + " -b, --clear-bad-sectors Clear the bad sector list\n" + " -d, --clear-dirty Clear the volume dirty flag\n" + " -h, --help Display this help\n" + " -n, --no-action Do not write anything\n" + " -V, --version Display version information\n" + "\n" + "For example: %s /dev/hda6\n\n", + EXEC_NAME, VERSION, EXEC_NAME, + EXEC_NAME); + ntfs_log_info("%s%s", ntfs_bugs, ntfs_home); + exit(ret); +} + +/** + * version + */ +__attribute__((noreturn)) +static void version(void) +{ + ntfs_log_info("%s v%s\n\n" + "Attempt to fix an NTFS partition.\n\n" + "Copyright (c) 2000-2006 Anton Altaparmakov\n" + "Copyright (c) 2002-2006 Szabolcs Szakacsits\n" + "Copyright (c) 2007 Yura Pakhuchiy\n" + "Copyright (c) 2011-2014 Jean-Pierre Andre\n\n", + EXEC_NAME, VERSION); + ntfs_log_info("%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); + exit(0); +} + +/** + * parse_options + */ +static void parse_options(int argc, char **argv) +{ + int c; + static const char *sopt = "-bdhnV"; + static const struct option lopt[] = { + { "help", no_argument, NULL, 'h' }, + { "no-action", no_argument, NULL, 'n' }, + { "clear-bad-sectors", no_argument, NULL, 'b' }, + { "clear-dirty", no_argument, NULL, 'd' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + memset(&opt, 0, sizeof(opt)); + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opt.volume) + opt.volume = argv[optind - 1]; + else { + ntfs_log_info("ERROR: Too many arguments.\n"); + usage(1); + } + break; + case 'b': + opt.clear_bad_sectors = TRUE; + break; + case 'd': + opt.clear_dirty = TRUE; + break; + case 'n': + opt.no_action = TRUE; + break; + case 'h': + usage(0); + case '?': + usage(1); + /* fall through */ + case 'V': + version(); + default: + ntfs_log_info("ERROR: Unknown option '%s'.\n", argv[optind - 1]); + usage(1); + } + } + + if (opt.volume == NULL) { + ntfs_log_info("ERROR: You must specify a device.\n"); + usage(1); + } +} + +/** + * OLD_ntfs_volume_set_flags + */ +static int OLD_ntfs_volume_set_flags(ntfs_volume *vol, const le16 flags) +{ + MFT_RECORD *m = NULL; + ATTR_RECORD *a; + VOLUME_INFORMATION *c; + ntfs_attr_search_ctx *ctx; + int ret = -1; /* failure */ + + if (!vol) { + errno = EINVAL; + return -1; + } + if (ntfs_file_record_read(vol, FILE_Volume, &m, NULL)) { + ntfs_log_perror("Failed to read $Volume"); + return -1; + } + /* Sanity check */ + if (!(m->flags & MFT_RECORD_IN_USE)) { + ntfs_log_error("$Volume has been deleted. Cannot handle this " + "yet. Run chkdsk to fix this.\n"); + errno = EIO; + goto err_exit; + } + /* Get a pointer to the volume information attribute. */ + ctx = ntfs_attr_get_search_ctx(NULL, m); + if (!ctx) { + ntfs_log_debug("Failed to allocate attribute search " + "context.\n"); + goto err_exit; + } + if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION was not found in " + "$Volume!\n"); + goto err_out; + } + a = ctx->attr; + /* Sanity check. */ + if (a->non_resident) { + ntfs_log_error("Attribute $VOLUME_INFORMATION must be resident " + "(and it isn't)!\n"); + errno = EIO; + goto err_out; + } + /* Get a pointer to the value of the attribute. */ + c = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); + /* Sanity checks. */ + if ((char*)c + le32_to_cpu(a->value_length) > + (char*)m + le32_to_cpu(m->bytes_in_use) || + le16_to_cpu(a->value_offset) + + le32_to_cpu(a->value_length) > le32_to_cpu(a->length)) { + ntfs_log_error("Attribute $VOLUME_INFORMATION in $Volume is " + "corrupt!\n"); + errno = EIO; + goto err_out; + } + /* Set the volume flags. */ + vol->flags = c->flags = flags; + if (ntfs_mft_record_write(vol, FILE_Volume, m)) { + ntfs_log_perror("Error writing $Volume"); + goto err_out; + } + ret = 0; /* success */ +err_out: + ntfs_attr_put_search_ctx(ctx); +err_exit: + free(m); + return ret; +} + +/** + * set_dirty_flag + */ +static int set_dirty_flag(ntfs_volume *vol) +{ + le16 flags; + + /* Porting note: We test for the current state of VOLUME_IS_DIRTY. This + * should actually be more appropriate than testing for NVolWasDirty. */ + if (vol->flags & VOLUME_IS_DIRTY) + return 0; + ntfs_log_info("Setting required flags on partition... "); + /* + * Set chkdsk flag, i.e. mark the partition dirty so chkdsk will run + * and fix it for us. + */ + flags = vol->flags | VOLUME_IS_DIRTY; + if (!opt.no_action && OLD_ntfs_volume_set_flags(vol, flags)) { + ntfs_log_info(FAILED); + ntfs_log_error("Error setting volume flags.\n"); + return -1; + } + vol->flags = flags; + + /* Porting note: libntfs-3g does not have the 'WasDirty' flag/property, + * and never touches the 'dirty' bit except when explicitly told to do + * so. Since we just wrote the VOLUME_IS_DIRTY bit to disk, and + * vol->flags is up-to-date, we can just ignore the NVolSetWasDirty + * statement. */ + /* NVolSetWasDirty(vol); */ + + ntfs_log_info(OK); + return 0; +} + +/** + * empty_journal + */ +static int empty_journal(ntfs_volume *vol) +{ + if (NVolLogFileEmpty(vol)) + return 0; + ntfs_log_info("Going to empty the journal ($LogFile)... "); + if (ntfs_logfile_reset(vol)) { + ntfs_log_info(FAILED); + ntfs_log_perror("Failed to reset $LogFile"); + return -1; + } + ntfs_log_info(OK); + return 0; +} + +/** + * Clear the bad cluster marks (option) + */ +static int clear_badclus(ntfs_volume *vol) +{ + static ntfschar badstream[] = { + const_cpu_to_le16('$'), const_cpu_to_le16('B'), + const_cpu_to_le16('a'), const_cpu_to_le16('d') + } ; + ntfs_inode *ni; + ntfs_attr *na; + BOOL ok; + + ok = FALSE; + ntfs_log_info("Going to un-mark the bad clusters ($BadClus)... "); + ni = ntfs_inode_open(vol, FILE_BadClus); + if (ni) { + na = ntfs_attr_open(ni, AT_DATA, badstream, 4); + /* + * chkdsk does not adjust the data size when + * moving clusters to $BadClus, so we have to + * check the runlist. + */ + if (na && !ntfs_attr_map_whole_runlist(na)) { + if (na->rl + && na->rl[0].length && na->rl[1].length) { + /* + * Truncate the stream to free all its clusters, + * (which requires setting the data size according + * to allocation), then reallocate a sparse stream + * to full size of volume and reset the data size. + */ + na->data_size = na->allocated_size; + na->initialized_size = na->allocated_size; + if (!ntfs_attr_truncate(na,0) + && !ntfs_attr_truncate(na,vol->nr_clusters + << vol->cluster_size_bits)) { + na->data_size = 0; + na->initialized_size = 0; + ni->flags |= FILE_ATTR_SPARSE_FILE; + NInoFileNameSetDirty(ni); + ok = TRUE; + } else { + ntfs_log_perror("Failed to un-mark the bad clusters"); + } + } else { + ntfs_log_info("No bad clusters..."); + ok = TRUE; + } + ntfs_attr_close(na); + } else { + ntfs_log_perror("Failed to open $BadClus::$Bad"); + } + ntfs_inode_close(ni); + } else { + ntfs_log_perror("Failed to open inode FILE_BadClus"); + } + if (ok) + ntfs_log_info(OK); + return (ok ? 0 : -1); +} + +/** + * fix_mftmirr + */ +static int fix_mftmirr(ntfs_volume *vol) +{ + s64 l, br; + unsigned char *m, *m2; + int i, ret = -1; /* failure */ + BOOL done; + + ntfs_log_info("\nProcessing $MFT and $MFTMirr...\n"); + + /* Load data from $MFT and $MFTMirr and compare the contents. */ + m = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); + if (!m) { + ntfs_log_perror("Failed to allocate memory"); + return -1; + } + m2 = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); + if (!m2) { + ntfs_log_perror("Failed to allocate memory"); + free(m); + return -1; + } + + ntfs_log_info("Reading $MFT... "); + l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, + vol->mft_record_size, m); + if (l != vol->mftmirr_size) { + ntfs_log_info(FAILED); + if (l != -1) + errno = EIO; + ntfs_log_perror("Failed to read $MFT"); + goto error_exit; + } + ntfs_log_info(OK); + + ntfs_log_info("Reading $MFTMirr... "); + l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, + vol->mft_record_size, m2); + if (l != vol->mftmirr_size) { + ntfs_log_info(FAILED); + if (l != -1) + errno = EIO; + ntfs_log_perror("Failed to read $MFTMirr"); + goto error_exit; + } + ntfs_log_info(OK); + + /* + * FIXME: Need to actually check the $MFTMirr for being real. Otherwise + * we might corrupt the partition if someone is experimenting with + * software RAID and the $MFTMirr is not actually in the position we + * expect it to be... )-: + * FIXME: We should emit a warning it $MFTMirr is damaged and ask + * user whether to recreate it from $MFT or whether to abort. - The + * warning needs to include the danger of software RAID arrays. + * Maybe we should go as far as to detect whether we are running on a + * MD disk and if yes then bomb out right at the start of the program? + */ + + ntfs_log_info("Comparing $MFTMirr to $MFT... "); + done = FALSE; + for (i = 0; i < vol->mftmirr_size; ++i) { + MFT_RECORD *mrec, *mrec2; + const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", + "$Volume", "$AttrDef", "root directory", "$Bitmap", + "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; + const char *s; + BOOL use_mirr; + + if (i < 12) + s = ESTR[i]; + else if (i < 16) + s = "system file"; + else + s = "mft record"; + + use_mirr = FALSE; + mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); + if (mrec->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_record(mrec->magic)) { + ntfs_log_info(FAILED); + ntfs_log_error("$MFT error: Incomplete multi " + "sector transfer detected in " + "%s.\nCannot handle this yet. " + ")-:\n", s); + goto error_exit; + } + if (!ntfs_is_mft_record(mrec->magic)) { + ntfs_log_info(FAILED); + ntfs_log_error("$MFT error: Invalid mft " + "record for %s.\nCannot " + "handle this yet. )-:\n", s); + goto error_exit; + } + } + mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); + if (mrec2->flags & MFT_RECORD_IN_USE) { + if (ntfs_is_baad_record(mrec2->magic)) { + ntfs_log_info(FAILED); + ntfs_log_error("$MFTMirr error: Incomplete " + "multi sector transfer " + "detected in %s.\n", s); + goto error_exit; + } + if (!ntfs_is_mft_record(mrec2->magic)) { + ntfs_log_info(FAILED); + ntfs_log_error("$MFTMirr error: Invalid mft " + "record for %s.\n", s); + goto error_exit; + } + /* $MFT is corrupt but $MFTMirr is ok, use $MFTMirr. */ + if (!(mrec->flags & MFT_RECORD_IN_USE) && + !ntfs_is_mft_record(mrec->magic)) + use_mirr = TRUE; + } + if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { + if (!done) { + done = TRUE; + ntfs_log_info(FAILED); + } + ntfs_log_info("Correcting differences in $MFT%s " + "record %d...", use_mirr ? "" : "Mirr", + i); + br = ntfs_mft_record_write(vol, i, + use_mirr ? mrec2 : mrec); + if (br) { + ntfs_log_info(FAILED); + ntfs_log_perror("Error correcting $MFT%s", + use_mirr ? "" : "Mirr"); + goto error_exit; + } + ntfs_log_info(OK); + } + } + if (!done) + ntfs_log_info(OK); + ntfs_log_info("Processing of $MFT and $MFTMirr completed " + "successfully.\n"); + ret = 0; +error_exit: + free(m); + free(m2); + return ret; +} + +/* + * Rewrite the $UpCase file as default + * + * Returns 0 if could be written + */ + +static int rewrite_upcase(ntfs_volume *vol, ntfs_attr *na) +{ + s64 l; + int res; + + /* writing the $UpCase may require bitmap updates */ + res = -1; + vol->lcnbmp_ni = ntfs_inode_open(vol, FILE_Bitmap); + if (!vol->lcnbmp_ni) { + ntfs_log_perror("Failed to open bitmap inode"); + } else { + vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, + AT_UNNAMED, 0); + if (!vol->lcnbmp_na) { + ntfs_log_perror("Failed to open bitmap data attribute"); + } else { + /* minimal consistency check on the bitmap */ + if (((vol->lcnbmp_na->data_size << 3) + < vol->nr_clusters) + || ((vol->lcnbmp_na->data_size << 3) + >= (vol->nr_clusters << 1)) + || (vol->lcnbmp_na->data_size + > vol->lcnbmp_na->allocated_size)) { + ntfs_log_error("Corrupt cluster map size %lld" + " (allocated %lld minimum %lld)\n", + (long long)vol->lcnbmp_na->data_size, + (long long)vol->lcnbmp_na->allocated_size, + (long long)(vol->nr_clusters + 7) >> 3); + } else { + ntfs_log_info("Rewriting $UpCase file\n"); + l = ntfs_attr_pwrite(na, 0, vol->upcase_len*2, + vol->upcase); + if (l != vol->upcase_len*2) { + ntfs_log_error("Failed to rewrite $UpCase\n"); + } else { + ntfs_log_info("$UpCase has been set to default\n"); + res = 0; + } + } + ntfs_attr_close(vol->lcnbmp_na); + vol->lcnbmp_na = (ntfs_attr*)NULL; + } + ntfs_inode_close(vol->lcnbmp_ni); + vol->lcnbmp_ni = (ntfs_inode*)NULL; + } + return (res); +} + +/* + * Fix the $UpCase file + * + * Returns 0 if the table is valid or has been fixed + */ + +static int fix_upcase(ntfs_volume *vol) +{ + ntfs_inode *ni; + ntfs_attr *na; + ntfschar *upcase; + s64 l; + u32 upcase_len; + u32 k; + int res; + + res = -1; + ni = (ntfs_inode*)NULL; + na = (ntfs_attr*)NULL; + /* Now load the upcase table from $UpCase. */ + ntfs_log_debug("Loading $UpCase...\n"); + ni = ntfs_inode_open(vol, FILE_UpCase); + if (!ni) { + ntfs_log_perror("Failed to open inode FILE_UpCase"); + goto error_exit; + } + /* Get an ntfs attribute for $UpCase/$DATA. */ + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ntfs_log_perror("Failed to open ntfs attribute"); + goto error_exit; + } + /* + * Note: Normally, the upcase table has a length equal to 65536 + * 2-byte Unicode characters but allow for different cases, so no + * checks done. Just check we don't overflow 32-bits worth of Unicode + * characters. + */ + if (na->data_size & ~0x1ffffffffULL) { + ntfs_log_error("Error: Upcase table is too big (max 32-bit " + "allowed).\n"); + errno = EINVAL; + goto error_exit; + } + upcase_len = na->data_size >> 1; + upcase = (ntfschar*)ntfs_malloc(na->data_size); + if (!upcase) + goto error_exit; + /* Read in the $DATA attribute value into the buffer. */ + l = ntfs_attr_pread(na, 0, na->data_size, upcase); + if (l != na->data_size) { + ntfs_log_error("Failed to read $UpCase, unexpected length " + "(%lld != %lld).\n", (long long)l, + (long long)na->data_size); + errno = EIO; + goto error_exit; + } + /* Consistency check of $UpCase, restricted to plain ASCII chars */ + k = 0x20; + while ((k < upcase_len) + && (k < 0x7f) + && (le16_to_cpu(upcase[k]) + == ((k < 'a') || (k > 'z') ? k : k + 'A' - 'a'))) + k++; + if (k < 0x7f) { + ntfs_log_error("Corrupted file $UpCase\n"); + if (!opt.no_action) { + /* rewrite the $UpCase file from default */ + res = rewrite_upcase(vol, na); + /* free the bad upcase record */ + if (!res) + free(upcase); + } else { + /* keep the default upcase but return an error */ + free(upcase); + } + } else { + /* accept the upcase table read from $UpCase */ + free(vol->upcase); + vol->upcase = upcase; + vol->upcase_len = upcase_len; + res = 0; + } +error_exit : + /* Done with the $UpCase mft record. */ + if (na) + ntfs_attr_close(na); + if (ni && ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close $UpCase"); + } + return (res); +} + +/* + * Rewrite the boot sector + * + * Returns 0 if successful + */ + +static int rewrite_boot(struct ntfs_device *dev, char *full_bs, + s32 sector_size) +{ + s64 bw; + int res; + + res = -1; + ntfs_log_info("Rewriting the bootsector\n"); + bw = ntfs_pwrite(dev, 0, sector_size, full_bs); + if (bw == sector_size) + res = 0; + else { + if (bw != -1) + errno = EINVAL; + if (!bw) + ntfs_log_error("Failed to rewrite the bootsector (size=0)\n"); + else + ntfs_log_perror("Error rewriting the bootsector"); + } + return (res); +} + +/* + * Locate an unnamed attribute in an MFT record + * + * Returns NULL if not found (with no error message) + */ + +static ATTR_RECORD *find_unnamed_attr(MFT_RECORD *mrec, ATTR_TYPES type) +{ + ATTR_RECORD *a; + u32 offset; + + /* fetch the requested attribute */ + offset = le16_to_cpu(mrec->attrs_offset); + a = (ATTR_RECORD*)((char*)mrec + offset); + while ((a->type != AT_END) + && ((a->type != type) || a->name_length) + && (offset < le32_to_cpu(mrec->bytes_in_use))) { + offset += le32_to_cpu(a->length); + a = (ATTR_RECORD*)((char*)mrec + offset); + } + if ((a->type != type) + || a->name_length) + a = (ATTR_RECORD*)NULL; + return (a); +} + +/* + * First condition for having a self-located MFT : + * only 16 MFT records are defined in MFT record 0 + * + * Only low-level library functions can be used. + * + * Returns TRUE if the condition is met. + */ + +static BOOL short_mft_selfloc_condition(struct MFT_SELF_LOCATED *selfloc) +{ + BOOL ok; + ntfs_volume *vol; + MFT_RECORD *mft0; + ATTR_RECORD *a; + runlist_element *rl; + u16 seqn; + + ok = FALSE; + vol = selfloc->vol; + mft0 = selfloc->mft0; + if ((ntfs_pread(vol->dev, + vol->mft_lcn << vol->cluster_size_bits, + vol->mft_record_size, mft0) + == vol->mft_record_size) + && !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft0, + vol->mft_record_size)) { + a = find_unnamed_attr(mft0,AT_DATA); + if (a + && a->non_resident + && (((le64_to_cpu(a->highest_vcn) + 1) + << vol->cluster_size_bits) + == (SELFLOC_LIMIT*vol->mft_record_size))) { + rl = ntfs_mapping_pairs_decompress(vol, a, NULL); + if (rl) { + /* + * The first error condition is having only + * 16 entries mapped in the first MFT record. + */ + if ((rl[0].lcn >= 0) + && ((rl[0].length << vol->cluster_size_bits) + == SELFLOC_LIMIT*vol->mft_record_size) + && (rl[1].vcn == rl[0].length) + && (rl[1].lcn == LCN_RL_NOT_MAPPED)) { + ok = TRUE; + seqn = le16_to_cpu( + mft0->sequence_number); + selfloc->mft_ref0 + = ((MFT_REF)seqn) << 48; + } + free(rl); + } + } + } + return (ok); +} + +/* + * Second condition for having a self-located MFT : + * The 16th MFT record is defined in MFT record >= 16 + * + * Only low-level library functions can be used. + * + * Returns TRUE if the condition is met. + */ + +static BOOL attrlist_selfloc_condition(struct MFT_SELF_LOCATED *selfloc) +{ + ntfs_volume *vol; + ATTR_RECORD *a; + ATTR_LIST_ENTRY *attrlist; + ATTR_LIST_ENTRY *al; + runlist_element *rl; + VCN vcn; + leVCN levcn; + u32 length; + int ok; + + ok = FALSE; + length = 0; + vol = selfloc->vol; + a = find_unnamed_attr(selfloc->mft0,AT_ATTRIBUTE_LIST); + if (a) { + selfloc->attrlist_resident = !a->non_resident; + selfloc->attrlist_lcn = 0; + if (a->non_resident) { + attrlist = selfloc->attrlist; + rl = ntfs_mapping_pairs_decompress(vol, a, NULL); + if (rl + && (rl->lcn >= 0) + && (le64_to_cpu(a->data_size) < vol->cluster_size) + && (ntfs_pread(vol->dev, + rl->lcn << vol->cluster_size_bits, + vol->cluster_size, attrlist) == vol->cluster_size)) { + selfloc->attrlist_lcn = rl->lcn; + al = attrlist; + length = le64_to_cpu(a->data_size); + } + } else { + al = (ATTR_LIST_ENTRY*) + ((char*)a + le16_to_cpu(a->value_offset)); + length = le32_to_cpu(a->value_length); + } + if (length) { + /* search for a data attribute defining entry 16 */ + vcn = (SELFLOC_LIMIT*vol->mft_record_size) + >> vol->cluster_size_bits; + levcn = cpu_to_le64(vcn); + while ((length > 0) + && al->length + && ((al->type != AT_DATA) + || ((leVCN)al->lowest_vcn != levcn))) { + length -= le16_to_cpu(al->length); + al = (ATTR_LIST_ENTRY*) + ((char*)al + le16_to_cpu(al->length)); + } + if ((length > 0) + && al->length + && (al->type == AT_DATA) + && !al->name_length + && ((leVCN)al->lowest_vcn == levcn) + && (MREF_LE(al->mft_reference) >= SELFLOC_LIMIT)) { + selfloc->mft_ref1 + = le64_to_cpu(al->mft_reference); + selfloc->attrlist_to_ref1 = al; + ok = TRUE; + } + } + } + return (ok); +} + +/* + * Third condition for having a self-located MFT : + * The location of the second part of the MFT is defined in itself + * + * To locate the second part, we have to assume the first and the + * second part of the MFT data are contiguous. + * + * Only low-level library functions can be used. + * + * Returns TRUE if the condition is met. + */ + +static BOOL self_mapped_selfloc_condition(struct MFT_SELF_LOCATED *selfloc) +{ + BOOL ok; + s64 inum; + u64 offs; + VCN lowest_vcn; + MFT_RECORD *mft1; + ATTR_RECORD *a; + ntfs_volume *vol; + runlist_element *rl; + + ok = FALSE; + vol = selfloc->vol; + mft1 = selfloc->mft1; + inum = MREF(selfloc->mft_ref1); + offs = (vol->mft_lcn << vol->cluster_size_bits) + + (inum << vol->mft_record_size_bits); + if ((ntfs_pread(vol->dev, offs, vol->mft_record_size, + mft1) == vol->mft_record_size) + && !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft1, + vol->mft_record_size)) { + lowest_vcn = (SELFLOC_LIMIT*vol->mft_record_size) + >> vol->cluster_size_bits; + a = find_unnamed_attr(mft1,AT_DATA); + if (a + && (mft1->flags & MFT_RECORD_IN_USE) + && ((VCN)le64_to_cpu(a->lowest_vcn) == lowest_vcn) + && (le64_to_cpu(mft1->base_mft_record) + == selfloc->mft_ref0) + && ((u16)MSEQNO(selfloc->mft_ref1) + == le16_to_cpu(mft1->sequence_number))) { + rl = ntfs_mapping_pairs_decompress(vol, a, NULL); + if ((rl[0].lcn == LCN_RL_NOT_MAPPED) + && !rl[0].vcn + && (rl[0].length == lowest_vcn) + && (rl[1].vcn == lowest_vcn) + && ((u64)(rl[1].lcn << vol->cluster_size_bits) + <= offs) + && ((u64)((rl[1].lcn + rl[1].length) + << vol->cluster_size_bits) > offs)) { + ok = TRUE; + } + } + } + return (ok); +} + +/* + * Fourth condition, to be able to fix a self-located MFT : + * The MFT record 15 must be available. + * + * The MFT record 15 is expected to be marked in use, we assume + * it is available if it has no parent, no name and no attr list. + * + * Only low-level library functions can be used. + * + * Returns TRUE if the condition is met. + */ + +static BOOL spare_record_selfloc_condition(struct MFT_SELF_LOCATED *selfloc) +{ + BOOL ok; + s64 inum; + u64 offs; + MFT_RECORD *mft2; + ntfs_volume *vol; + + ok = FALSE; + vol = selfloc->vol; + mft2 = selfloc->mft2; + inum = SELFLOC_LIMIT - 1; + offs = (vol->mft_lcn << vol->cluster_size_bits) + + (inum << vol->mft_record_size_bits); + if ((ntfs_pread(vol->dev, offs, vol->mft_record_size, + mft2) == vol->mft_record_size) + && !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft2, + vol->mft_record_size)) { + if (!mft2->base_mft_record + && (mft2->flags & MFT_RECORD_IN_USE) + && !find_unnamed_attr(mft2,AT_ATTRIBUTE_LIST) + && !find_unnamed_attr(mft2,AT_FILE_NAME)) { + ok = TRUE; + } + } + return (ok); +} + +/* + * Fix a self-located MFT by swapping two MFT records + * + * Only low-level library functions can be used. + * + * Returns 0 if the MFT corruption could be fixed. + */ +static int fix_selfloc_conditions(struct MFT_SELF_LOCATED *selfloc) +{ + MFT_RECORD *mft1; + MFT_RECORD *mft2; + ATTR_RECORD *a; + ATTR_LIST_ENTRY *al; + ntfs_volume *vol; + s64 offs; + s64 offsm; + s64 offs1; + s64 offs2; + s64 inum; + u16 usa_ofs; + int res; + + res = 0; + /* + * In MFT1, we must fix : + * - the self-reference, if present, + * - its own sequence number, must be 15 + * - the sizes of the data attribute. + */ + vol = selfloc->vol; + mft1 = selfloc->mft1; + mft2 = selfloc->mft2; + usa_ofs = le16_to_cpu(mft1->usa_ofs); + if (usa_ofs >= 48) + mft1->mft_record_number = const_cpu_to_le32(SELFLOC_LIMIT - 1); + mft1->sequence_number = const_cpu_to_le16(SELFLOC_LIMIT - 1); + a = find_unnamed_attr(mft1,AT_DATA); + if (a) { + a->allocated_size = const_cpu_to_le64(0); + a->data_size = const_cpu_to_le64(0); + a->initialized_size = const_cpu_to_le64(0); + } else + res = -1; /* bug : it has been found earlier */ + + /* + * In MFT2, we must fix : + * - the self-reference, if present + */ + usa_ofs = le16_to_cpu(mft2->usa_ofs); + if (usa_ofs >= 48) + mft2->mft_record_number = cpu_to_le32(MREF(selfloc->mft_ref1)); + + /* + * In the attribute list, we must fix : + * - the reference to MFT1 + */ + al = selfloc->attrlist_to_ref1; + al->mft_reference = MK_LE_MREF(SELFLOC_LIMIT - 1, SELFLOC_LIMIT - 1); + + /* + * All fixes done, we can write all if allowed + */ + if (!res && !opt.no_action) { + inum = SELFLOC_LIMIT - 1; + offs2 = (vol->mft_lcn << vol->cluster_size_bits) + + (inum << vol->mft_record_size_bits); + inum = MREF(selfloc->mft_ref1); + offs1 = (vol->mft_lcn << vol->cluster_size_bits) + + (inum << vol->mft_record_size_bits); + + /* rewrite the attribute list */ + if (selfloc->attrlist_resident) { + /* write mft0 and mftmirr if it is resident */ + offs = vol->mft_lcn << vol->cluster_size_bits; + offsm = vol->mftmirr_lcn << vol->cluster_size_bits; + if (ntfs_mst_pre_write_fixup( + (NTFS_RECORD*)selfloc->mft0, + vol->mft_record_size) + || (ntfs_pwrite(vol->dev, offs, vol->mft_record_size, + selfloc->mft0) != vol->mft_record_size) + || (ntfs_pwrite(vol->dev, offsm, vol->mft_record_size, + selfloc->mft0) != vol->mft_record_size)) + res = -1; + } else { + /* write a full cluster if non resident */ + offs = selfloc->attrlist_lcn << vol->cluster_size_bits; + if (ntfs_pwrite(vol->dev, offs, vol->cluster_size, + selfloc->attrlist) != vol->cluster_size) + res = -1; + } + /* replace MFT2 by MFT1 and replace MFT1 by MFT2 */ + if (!res + && (ntfs_mst_pre_write_fixup((NTFS_RECORD*)selfloc->mft1, + vol->mft_record_size) + || ntfs_mst_pre_write_fixup((NTFS_RECORD*)selfloc->mft2, + vol->mft_record_size) + || (ntfs_pwrite(vol->dev, offs2, vol->mft_record_size, + mft1) != vol->mft_record_size) + || (ntfs_pwrite(vol->dev, offs1, vol->mft_record_size, + mft2) != vol->mft_record_size))) + res = -1; + } + return (res); +} + +/* + * Detect and fix a Windows XP bug, leading to a corrupt MFT + * + * Windows cannot boot anymore, so chkdsk cannot be started, which + * is a good point, because chkdsk would have deleted all the files. + * Older ntfs-3g fell into an endless recursion (recent versions + * refuse to mount). + * + * This situation is very rare, but it was fun to fix it. + * + * The corrupted condition is : + * - MFT entry 0 has only the runlist for MFT entries 0-15 + * - The attribute list for MFT shows the second part + * in an MFT record beyond 15 + * Of course, this record has to be read in order to know where it is. + * + * Sample case, met in 2011 (Windows XP) : + * MFT record 0 has : stdinfo, nonres attrlist, the first + * part of MFT data (entries 0-15), and bitmap + * MFT record 16 has the name + * MFT record 17 has the third part of MFT data (16-117731) + * MFT record 18 has the second part of MFT data (117732-170908) + * + * Assuming the second part of the MFT is contiguous to the first + * part, we can find it, and fix the condition by relocating it + * and swapping it with MFT record 15. + * This record number 15 appears to be hardcoded into Windows NTFS. + * + * Only low-level library functions can be used. + * + * Returns 0 if the conditions for the error were not met or + * the error could be fixed, + * -1 if some error was encountered + */ + +static int fix_self_located_mft(ntfs_volume *vol) +{ + struct MFT_SELF_LOCATED selfloc; + BOOL res; + + ntfs_log_info("Checking for self-located MFT segment... "); + res = -1; + selfloc.vol = vol; + selfloc.mft0 = (MFT_RECORD*)malloc(vol->mft_record_size); + selfloc.mft1 = (MFT_RECORD*)malloc(vol->mft_record_size); + selfloc.mft2 = (MFT_RECORD*)malloc(vol->mft_record_size); + selfloc.attrlist = (ATTR_LIST_ENTRY*)malloc(vol->cluster_size); + if (selfloc.mft0 && selfloc.mft1 && selfloc.mft2 + && selfloc.attrlist) { + if (short_mft_selfloc_condition(&selfloc) + && attrlist_selfloc_condition(&selfloc) + && self_mapped_selfloc_condition(&selfloc) + && spare_record_selfloc_condition(&selfloc)) { + ntfs_log_info(FOUND); + ntfs_log_info("Fixing the self-located MFT segment... "); + res = fix_selfloc_conditions(&selfloc); + ntfs_log_info(res ? FAILED : OK); + } else { + ntfs_log_info(OK); + res = 0; + } + free(selfloc.mft0); + free(selfloc.mft1); + free(selfloc.mft2); + free(selfloc.attrlist); + } + return (res); +} + +/* + * Try an alternate boot sector and fix the real one + * + * Only after successful checks is the boot sector rewritten. + * + * The alternate boot sector is not rewritten, either because it + * was found correct, or because we truncated the file system + * and the last actual sector might be part of some file. + * + * Returns 0 if successful + */ + +static int try_fix_boot(ntfs_volume *vol, char *full_bs, + s64 read_sector, s64 fix_sectors, s32 sector_size) +{ + s64 br; + int res; + s64 got_sectors; + le16 sector_size_le; + NTFS_BOOT_SECTOR *bs; + + res = -1; + br = ntfs_pread(vol->dev, read_sector*sector_size, + sector_size, full_bs); + if (br != sector_size) { + if (br != -1) + errno = EINVAL; + if (!br) + ntfs_log_error("Failed to read alternate bootsector (size=0)\n"); + else + ntfs_log_perror("Error reading alternate bootsector"); + } else { + bs = (NTFS_BOOT_SECTOR*)full_bs; + got_sectors = le64_to_cpu(bs->number_of_sectors); + bs->number_of_sectors = cpu_to_le64(fix_sectors); + /* alignment problem on Sparc, even doing memcpy() */ + sector_size_le = cpu_to_le16(sector_size); + if (!memcmp(§or_size_le, &bs->bpb.bytes_per_sector,2) + && ntfs_boot_sector_is_ntfs(bs) + && !ntfs_boot_sector_parse(vol, bs)) { + ntfs_log_info("The alternate bootsector is usable\n"); + if (fix_sectors != got_sectors) + ntfs_log_info("Set sector count to %lld instead of %lld\n", + (long long)fix_sectors, + (long long)got_sectors); + /* fix the normal boot sector */ + if (!opt.no_action) { + res = rewrite_boot(vol->dev, full_bs, + sector_size); + } else + res = 0; + } + if (!res && !opt.no_action) + ntfs_log_info("The boot sector has been rewritten\n"); + } + return (res); +} + +/* + * Try the alternate boot sector if the normal one is bad + * + * Actually : + * - first try the last sector of the partition (expected location) + * - then try the last sector as shown in the main boot sector, + * (could be meaningful for an undersized partition) + * - finally try truncating the file system actual size of partition + * (could be meaningful for an oversized partition) + * + * if successful, rewrite the normal boot sector accordingly + * + * Returns 0 if successful + */ + +static int try_alternate_boot(ntfs_volume *vol, char *full_bs, + s32 sector_size, s64 shown_sectors) +{ + s64 actual_sectors; + int res; + + res = -1; + ntfs_log_info("Trying the alternate boot sector\n"); + + /* + * We do not rely on the sector size defined in the + * boot sector, supposed to be corrupt, so we try to get + * the actual sector size and defaulting to 512 if failed + * to get. This value is only used to guess the alternate + * boot sector location and it is checked against the + * value found in the sector itself. It should not damage + * anything if wrong. + * + * Note : the real last sector is not accounted for here. + */ + actual_sectors = ntfs_device_size_get(vol->dev,sector_size) - 1; + + /* first try the actual last sector */ + if ((actual_sectors > 0) + && !try_fix_boot(vol, full_bs, actual_sectors, + actual_sectors, sector_size)) + res = 0; + + /* then try the shown last sector, if less than actual */ + if (res + && (shown_sectors > 0) + && (shown_sectors < actual_sectors) + && !try_fix_boot(vol, full_bs, shown_sectors, + shown_sectors, sector_size)) + res = 0; + + /* then try reducing the number of sectors to actual value */ + if (res + && (shown_sectors > actual_sectors) + && !try_fix_boot(vol, full_bs, 0, actual_sectors, sector_size)) + res = 0; + + return (res); +} + +/* + * Check and fix the alternate boot sector + * + * The alternate boot sector is usually in the last sector of a + * partition, which should not be used by the file system + * (the sector count in the boot sector should be less than + * the total sector count in the partition). + * + * chkdsk never changes the count in the boot sector. + * - If this is less than the total count, chkdsk place the + * alternate boot sector into the sector, + * - if the count is the same as the total count, chkdsk place + * the alternate boot sector into the middle sector (half + * the total count rounded upwards) + * - if the count is greater than the total count, chkdsk + * declares the file system as raw, and refuses to fix anything. + * + * Here, we check and fix the alternate boot sector, only in the + * first situation where the file system does not overflow on the + * last sector. + * + * Note : when shrinking a partition, ntfsresize cannot determine + * the future size of the partition. As a consequence the number of + * sectors in the boot sectors may be less than the possible size. + * + * Returns 0 if successful + */ + +static int check_alternate_boot(ntfs_volume *vol) +{ + s64 got_sectors; + s64 actual_sectors; + s64 last_sector_off; + char *full_bs; + char *alt_bs; + NTFS_BOOT_SECTOR *bs; + s64 br; + s64 bw; + int res; + + res = -1; + full_bs = (char*)malloc(vol->sector_size); + alt_bs = (char*)malloc(vol->sector_size); + if (!full_bs || !alt_bs) { + ntfs_log_info("Error : failed to allocate memory\n"); + goto error_exit; + } + /* Now read both bootsectors. */ + br = ntfs_pread(vol->dev, 0, vol->sector_size, full_bs); + if (br == vol->sector_size) { + bs = (NTFS_BOOT_SECTOR*)full_bs; + got_sectors = le64_to_cpu(bs->number_of_sectors); + actual_sectors = ntfs_device_size_get(vol->dev, + vol->sector_size); + if (actual_sectors > got_sectors) { + last_sector_off = (actual_sectors - 1) + << vol->sector_size_bits; + ntfs_log_info("Checking the alternate boot sector... "); + br = ntfs_pread(vol->dev, last_sector_off, + vol->sector_size, alt_bs); + } else { + ntfs_log_info("Checking file system overflow... "); + br = -1; + } + /* accept getting no byte, needed for short image files */ + if (br >= 0) { + if ((br != vol->sector_size) + || memcmp(full_bs, alt_bs, vol->sector_size)) { + if (opt.no_action) { + ntfs_log_info("BAD\n"); + } else { + bw = ntfs_pwrite(vol->dev, + last_sector_off, + vol->sector_size, full_bs); + if (bw == vol->sector_size) { + ntfs_log_info("FIXED\n"); + res = 0; + } else { + ntfs_log_info(FAILED); + } + } + } else { + ntfs_log_info(OK); + res = 0; + } + } else { + ntfs_log_info(FAILED); + } + } else { + ntfs_log_info("Error : could not read the boot sector again\n"); + } + free(full_bs); + free(alt_bs); + +error_exit : + return (res); +} + +/* + * Try to fix problems which may arise in the start up sequence + * + * This is a replay of the normal start up sequence with fixes when + * some problem arise. + */ + +static int fix_startup(struct ntfs_device *dev, unsigned long flags) +{ + s64 br; + ntfs_volume *vol; + BOOL dev_open; + s64 shown_sectors; + char *full_bs; + NTFS_BOOT_SECTOR *bs; + s32 sector_size; + int res; + int eo; + + errno = 0; + res = -1; + dev_open = FALSE; + full_bs = (char*)NULL; + if (!dev || !dev->d_ops || !dev->d_name) { + errno = EINVAL; + ntfs_log_perror("%s: dev = %p", __FUNCTION__, dev); + vol = (ntfs_volume*)NULL; + goto error_exit; + } + + /* Allocate the volume structure. */ + vol = ntfs_volume_alloc(); + if (!vol) + goto error_exit; + + /* Create the default upcase table. */ + vol->upcase_len = ntfs_upcase_build_default(&vol->upcase); + if (!vol->upcase_len || !vol->upcase) + goto error_exit; + + /* Default with no locase table and case sensitive file names */ + vol->locase = (ntfschar*)NULL; + NVolSetCaseSensitive(vol); + + /* by default, all files are shown and not marked hidden */ + NVolSetShowSysFiles(vol); + NVolSetShowHidFiles(vol); + NVolClearHideDotFiles(vol); + if (flags & NTFS_MNT_RDONLY) + NVolSetReadOnly(vol); + + /* ...->open needs bracketing to compile with glibc 2.7 */ + if ((dev->d_ops->open)(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) { + ntfs_log_perror("Error opening '%s'", dev->d_name); + goto error_exit; + } + dev_open = TRUE; + /* Attach the device to the volume. */ + vol->dev = dev; + + sector_size = ntfs_device_sector_size_get(dev); + if (sector_size <= 0) + sector_size = DEFAULT_SECTOR_SIZE; + full_bs = (char*)malloc(sector_size); + if (!full_bs) + goto error_exit; + /* Now read the bootsector. */ + br = ntfs_pread(dev, 0, sector_size, full_bs); + if (br != sector_size) { + if (br != -1) + errno = EINVAL; + if (!br) + ntfs_log_error("Failed to read bootsector (size=0)\n"); + else + ntfs_log_perror("Error reading bootsector"); + goto error_exit; + } + bs = (NTFS_BOOT_SECTOR*)full_bs; + if (!ntfs_boot_sector_is_ntfs(bs) + /* get the bootsector data, only fails when inconsistent */ + || (ntfs_boot_sector_parse(vol, bs) < 0)) { + shown_sectors = le64_to_cpu(bs->number_of_sectors); + /* boot sector is wrong, try the alternate boot sector */ + if (try_alternate_boot(vol, full_bs, sector_size, + shown_sectors)) { + errno = EINVAL; + goto error_exit; + } + res = 0; + } else { + res = fix_self_located_mft(vol); + } +error_exit: + if (res) { + switch (errno) { + case ENOMEM : + ntfs_log_error("Failed to allocate memory\n"); + break; + case EINVAL : + ntfs_log_error("Unrecoverable error\n"); + break; + default : + break; + } + } + eo = errno; + free(full_bs); + if (vol) { + free(vol->upcase); + free(vol); + } + if (dev_open) { + (dev->d_ops->close)(dev); + } + errno = eo; + return (res); +} + +/** + * fix_mount + */ +static int fix_mount(void) +{ + int ret = 0; /* default success */ + ntfs_volume *vol; + struct ntfs_device *dev; + unsigned long flags; + + ntfs_log_info("Attempting to correct errors... "); + + dev = ntfs_device_alloc(opt.volume, 0, &ntfs_device_default_io_ops, + NULL); + if (!dev) { + ntfs_log_info(FAILED); + ntfs_log_perror("Failed to allocate device"); + return -1; + } + flags = (opt.no_action ? NTFS_MNT_RDONLY : 0); + vol = ntfs_volume_startup(dev, flags); + if (!vol) { + ntfs_log_info(FAILED); + ntfs_log_perror("Failed to startup volume"); + + /* Try fixing the bootsector and MFT, then redo the startup */ + if (!fix_startup(dev, flags)) { + if (opt.no_action) + ntfs_log_info("The startup data can be fixed, " + "but no change was requested\n"); + else + vol = ntfs_volume_startup(dev, flags); + } + if (!vol) { + ntfs_log_error("Volume is corrupt. You should run chkdsk.\n"); + ntfs_device_free(dev); + return -1; + } + if (opt.no_action) + ret = -1; /* error present and not fixed */ + } + /* if option -n proceed despite errors, to display them all */ + if ((!ret || opt.no_action) && (fix_mftmirr(vol) < 0)) + ret = -1; + if ((!ret || opt.no_action) && (fix_upcase(vol) < 0)) + ret = -1; + if ((!ret || opt.no_action) && (set_dirty_flag(vol) < 0)) + ret = -1; + if ((!ret || opt.no_action) && (empty_journal(vol) < 0)) + ret = -1; + /* + * ntfs_umount() will invoke ntfs_device_free() for us. + * Ignore the returned error resulting from partial mounting. + */ + ntfs_umount(vol, 1); + return ret; +} + +/** + * main + */ +int main(int argc, char **argv) +{ + ntfs_volume *vol; + unsigned long mnt_flags; + unsigned long flags; + int ret = 1; /* failure */ + BOOL force = FALSE; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + parse_options(argc, argv); + + if (!ntfs_check_if_mounted(opt.volume, &mnt_flags)) { + if ((mnt_flags & NTFS_MF_MOUNTED) && + !(mnt_flags & NTFS_MF_READONLY) && !force) { + ntfs_log_error("Refusing to operate on read-write " + "mounted device %s.\n", opt.volume); + exit(1); + } + } else + ntfs_log_perror("Failed to determine whether %s is mounted", + opt.volume); + /* Attempt a full mount first. */ + flags = (opt.no_action ? NTFS_MNT_RDONLY : 0); + ntfs_log_info("Mounting volume... "); + vol = ntfs_mount(opt.volume, flags); + if (vol) { + ntfs_log_info(OK); + ntfs_log_info("Processing of $MFT and $MFTMirr completed " + "successfully.\n"); + } else { + ntfs_log_info(FAILED); + if (fix_mount() < 0) { + if (opt.no_action) + ntfs_log_info("No change made\n"); + exit(1); + } + vol = ntfs_mount(opt.volume, 0); + if (!vol) { + ntfs_log_perror("Remount failed"); + exit(1); + } + } + if (check_alternate_boot(vol)) { + ntfs_log_error("Error: Failed to fix the alternate boot sector\n"); + exit(1); + } + /* So the unmount does not clear it again. */ + + /* Porting note: The WasDirty flag was set here to prevent ntfs_unmount + * from clearing the dirty bit (which might have been set in + * fix_mount()). So the intention is to leave the dirty bit set. + * + * libntfs-3g does not automatically set or clear dirty flags on + * mount/unmount, this means that the assumption that the dirty flag is + * now set does not hold. So we need to set it if not already set. + * + * However clear the flag if requested to do so, at this stage + * mounting was successful. + */ + if (opt.clear_dirty) + vol->flags &= ~VOLUME_IS_DIRTY; + else + vol->flags |= VOLUME_IS_DIRTY; + if (!opt.no_action && ntfs_volume_write_flags(vol, vol->flags)) { + ntfs_log_error("Error: Failed to set volume dirty flag (%d " + "(%s))!\n", errno, strerror(errno)); + } + + /* Check NTFS version is ok for us (in $Volume) */ + ntfs_log_info("NTFS volume version is %i.%i.\n", vol->major_ver, + vol->minor_ver); + if (ntfs_version_is_supported(vol)) { + ntfs_log_error("Error: Unknown NTFS version.\n"); + goto error_exit; + } + if (opt.clear_bad_sectors && !opt.no_action) { + if (clear_badclus(vol)) { + ntfs_log_error("Error: Failed to un-mark bad sectors.\n"); + goto error_exit; + } + } + if (vol->major_ver >= 3) { + /* + * FIXME: If on NTFS 3.0+, check for presence of the usn + * journal and stamp it if present. + */ + } + /* FIXME: We should be marking the quota out of date, too. */ + /* That's all for now! */ + ntfs_log_info("NTFS partition %s was processed successfully.\n", + vol->dev->d_name); + /* Set return code to 0. */ + ret = 0; +error_exit: + if (ntfs_umount(vol, 0)) + ntfs_umount(vol, 1); + if (ret) + exit(ret); + return ret; +} + diff --git a/ntfsprogs/ntfsinfo.8 b/ntfsprogs/ntfsinfo.8 new file mode 100755 index 0000000000000000000000000000000000000000..70550c95326835e266f7bfea09ccf0c0c48980d9 --- /dev/null +++ b/ntfsprogs/ntfsinfo.8 @@ -0,0 +1,89 @@ +.\" Copyright (c) 2002\-2004 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSINFO 8 "April 2006" "ntfs-3g 2015.3.14" +.SH NAME +ntfsinfo \- dump a file's attributes +.SH SYNOPSIS +.B ntfsinfo +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfsinfo +will dump the attributes of inode +.I inode\-number +or the file +.I path\-filename +and/or information about the mft ( +.I \-m +option). +Run ntfsinfo without arguments for a full list of options. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsinfo +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-F\fR, \fB\-\-file\fR FILE +Show information about this file +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not overwriting an existing +file. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-i\fR, \fB\-\-inode\fR NUM +Show information about this inode. +.TP +\fB\-m\fR, \fB\-\-mft\fR +Show information about the volume. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Produce less output. +.TP +\fB\-t\fR, \fB\-\-notime\fR +Do not display timestamps in the output. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfsinfo +prints. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license. +.SH BUGS +There are no known problems with +.BR ntfsinfo . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfsinfo +was written by Matthew J. Fanto, Anton Altaparmakov, Richard Russon, Szabolcs +Szakacsits, Yuval Fledel, Yura Pakhuchiy and Cristian Klein. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsinfo +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsinfo.8.in b/ntfsprogs/ntfsinfo.8.in new file mode 100755 index 0000000000000000000000000000000000000000..e0141c79bde974487cd36ad018ce1654343b768d --- /dev/null +++ b/ntfsprogs/ntfsinfo.8.in @@ -0,0 +1,89 @@ +.\" Copyright (c) 2002\-2004 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSINFO 8 "April 2006" "ntfs-3g @VERSION@" +.SH NAME +ntfsinfo \- dump a file's attributes +.SH SYNOPSIS +.B ntfsinfo +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfsinfo +will dump the attributes of inode +.I inode\-number +or the file +.I path\-filename +and/or information about the mft ( +.I \-m +option). +Run ntfsinfo without arguments for a full list of options. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsinfo +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-F\fR, \fB\-\-file\fR FILE +Show information about this file +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not overwriting an existing +file. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-i\fR, \fB\-\-inode\fR NUM +Show information about this inode. +.TP +\fB\-m\fR, \fB\-\-mft\fR +Show information about the volume. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Produce less output. +.TP +\fB\-t\fR, \fB\-\-notime\fR +Do not display timestamps in the output. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfsinfo +prints. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license. +.SH BUGS +There are no known problems with +.BR ntfsinfo . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfsinfo +was written by Matthew J. Fanto, Anton Altaparmakov, Richard Russon, Szabolcs +Szakacsits, Yuval Fledel, Yura Pakhuchiy and Cristian Klein. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsinfo +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c new file mode 100755 index 0000000000000000000000000000000000000000..817eadc45de715450796a8f74e261a3114fe64a5 --- /dev/null +++ b/ntfsprogs/ntfsinfo.c @@ -0,0 +1,2408 @@ +/** + * ntfsinfo - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2004 Matthew J. Fanto + * Copyright (c) 2002-2006 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2003-2006 Szabolcs Szakacsits + * Copyright (c) 2004-2005 Yuval Fledel + * Copyright (c) 2004-2007 Yura Pakhuchiy + * Copyright (c) 2005 Cristian Klein + * Copyright (c) 2011-2014 Jean-Pierre Andre + * + * This utility will dump a file's attributes. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * TODO LIST: + * - Better error checking. (focus on ntfs_dump_volume) + * - Comment things better. + * - More things at verbose mode. + * - Dump ACLs when security_id exists (NTFS 3+ only). + * - Clean ups. + * - Internationalization. + * - Add more Indexed Attr Types. + * - Make formatting look more like www.flatcap.org/ntfs/info + * + * Still not dumping certain attributes. Need to find the best + * way to output some of these attributes. + * + * Still need to do: + * $REPARSE_POINT/$SYMBOLIC_LINK + * $LOGGED_UTILITY_STREAM + */ + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "types.h" +#include "mft.h" +#include "attrib.h" +#include "layout.h" +#include "inode.h" +#include "index.h" +#include "utils.h" +#include "security.h" +#include "mst.h" +#include "dir.h" +#include "ntfstime.h" +/* #include "version.h" */ +#include "support.h" +#include "misc.h" + +static const char *EXEC_NAME = "ntfsinfo"; + +static struct options { + const char *device; /* Device/File to work with */ + const char *filename; /* Resolve this filename to mft number */ + s64 inode; /* Info for this inode */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int force; /* Override common sense */ + int notime; /* Don't report timestamps at all */ + int mft; /* Dump information about the volume as well */ +} opts; + +struct RUNCOUNT { + unsigned long runs; + unsigned long fragments; +} ; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + printf("\n%s v%s (libntfs-3g) - Display information about an NTFS " + "Volume.\n\n", EXEC_NAME, VERSION); + printf("Copyright (c)\n"); + printf(" 2002-2004 Matthew J. Fanto\n"); + printf(" 2002-2006 Anton Altaparmakov\n"); + printf(" 2002-2005 Richard Russon\n"); + printf(" 2003-2006 Szabolcs Szakacsits\n"); + printf(" 2003 Leonard NorrgÃ¥rd\n"); + printf(" 2004-2005 Yuval Fledel\n"); + printf(" 2004-2007 Yura Pakhuchiy\n"); + printf(" 2011-2014 Jean-Pierre Andre\n"); + printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + printf("\nUsage: %s [options] device\n" + " -i, --inode NUM Display information about this inode\n" + " -F, --file FILE Display information about this file (absolute path)\n" + " -m, --mft Dump information about the volume\n" + " -t, --notime Don't report timestamps\n" + "\n" + " -f, --force Use less caution\n" + " -q, --quiet Less output\n" + " -v, --verbose More output\n" + " -V, --version Display version information\n" + " -h, --help Display this help\n" + "\n", + EXEC_NAME); + printf("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char *argv[]) +{ + static const char *sopt = "-:dfhi:F:mqtTvV"; + static const struct option lopt[] = { + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "inode", required_argument, NULL, 'i' }, + { "file", required_argument, NULL, 'F' }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { "notime", no_argument, NULL, 'T' }, + { "mft", no_argument, NULL, 'm' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.inode = -1; + opts.filename = NULL; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: + if (!opts.device) + opts.device = optarg; + else + err++; + break; + case 'i': + if ((opts.inode != -1) || + (!utils_parse_size(optarg, &opts.inode, FALSE))) { + err++; + } + break; + case 'F': + if (opts.filename == NULL) { + /* The inode can not be resolved here, + store the filename */ + opts.filename = argv[optind-1]; + } else { + /* "-F" can't appear more than once */ + err++; + } + break; + case 'f': + opts.force++; + break; + case 'h': + help++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 't': + opts.notime++; + break; + case 'T': + /* 'T' is deprecated, notify */ + ntfs_log_error("Option 'T' is deprecated, it was " + "replaced by 't'.\n"); + err++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + case 'm': + opts.mft++; + break; + case '?': + if (optopt=='?') { + help++; + continue; + } + if (ntfs_log_parse_option(argv[optind-1])) + continue; + ntfs_log_error("Unknown option '%s'.\n", + argv[optind-1]); + err++; + break; + case ':': + ntfs_log_error("Option '%s' requires an " + "argument.\n", argv[optind-1]); + err++; + break; + default: + ntfs_log_error("Unhandled option case: %d.\n", c); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify exactly one " + "device.\n"); + err++; + } + + if (opts.inode == -1 && !opts.filename && !opts.mft) { + if (argc > 1) + ntfs_log_error("You must specify an inode to " + "learn about.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose " + "at the same time.\n"); + err++; + } + + if ((opts.inode != -1) && (opts.filename != NULL)) { + if (argc > 1) + ntfs_log_error("You may not specify --inode " + "and --file together.\n"); + err++; + } + + } + + if (ver) + version(); + if (help || err) + usage(); + + /* tri-state 0 : done, 1 : error, -1 : proceed */ + return (err ? 1 : (help || ver ? 0 : -1)); +} + + +/* *************** utility functions ******************** */ +/** + * ntfsinfo_time_to_str() - + * @sle_ntfs_clock: on disk time format in 100ns units since 1st jan 1601 + * in little-endian format + * + * Return char* in a format 'Thu Jan 1 00:00:00 1970'. + * No need to free the returned memory. + * + * Example of usage: + * char *time_str = ntfsinfo_time_to_str( + * sle64_to_cpu(standard_attr->creation_time)); + * printf("\tFile Creation Time:\t %s", time_str); + */ +static char *ntfsinfo_time_to_str(const sle64 sle_ntfs_clock) +{ + /* JPA display timestamps in UTC */ + static const char *months[] + = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" } ; + static const char *wdays[] + = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } ; + static char str[30]; + long long stamp; + u32 days; + u32 seconds; + unsigned int year; + unsigned int wday; + int mon; + int cnt; + + stamp = sle64_to_cpu(sle_ntfs_clock); + days = (stamp/(86400*10000000LL)) & 0x7ffff; + seconds = ((stamp/10000000LL)%86400) & 0x1ffff; + wday = (days + 1)%7; + year = 1601; + /* periods of 400 years */ + cnt = days/146097; + days -= 146097*cnt; + year += 400*cnt; + /* periods of 100 years */ + cnt = (3*days + 3)/109573; + days -= 36524*cnt; + year += 100*cnt; + /* periods of 4 years */ + cnt = days/1461; + days -= 1461*cnt; + year += 4*cnt; + /* periods of a single year */ + cnt = (3*days + 3)/1096; + days -= 365*cnt; + year += cnt; + + if ((!(year % 100) ? (year % 400) : (year % 4)) + && (days > 58)) days++; + if (days > 59) { + mon = (5*days + 161)/153; + days -= (153*mon - 162)/5; + } else { + mon = days/31 + 1; + days -= 31*(mon - 1) - 1; + } + sprintf(str,"%3s %3s %2u %02u:%02u:%02u %4u UTC\n", + wdays[wday], + months[mon-1],(unsigned int)days, + (unsigned int)(seconds/3600), + (unsigned int)(seconds/60%60), + (unsigned int)(seconds%60), + (unsigned int)year); + return (str); +} + +/** + * ntfs_attr_get_name() + * @attr: a valid attribute record + * + * return multi-byte string containing the attribute name if exist. the user + * is then responsible of freeing that memory. + * null if no name exists (attr->name_length==0). no memory allocated. + * null if cannot convert to multi-byte string. errno would contain the + * error id. no memory allocated in that case + */ +static char *ntfs_attr_get_name_mbs(ATTR_RECORD *attr) +{ + ntfschar *ucs_attr_name; + char *mbs_attr_name = NULL; + int mbs_attr_name_size; + + /* Get name in unicode. */ + ucs_attr_name = ntfs_attr_get_name(attr); + /* Convert unicode to printable format. */ + mbs_attr_name_size = ntfs_ucstombs(ucs_attr_name, attr->name_length, + &mbs_attr_name, 0); + if (mbs_attr_name_size > 0) + return mbs_attr_name; + else + return NULL; +} + + +/* *************** functions for dumping global info ******************** */ +/** + * ntfs_dump_volume - dump information about the volume + */ +static void ntfs_dump_volume(ntfs_volume *vol) +{ + printf("Volume Information \n"); + printf("\tName of device: %s\n", vol->dev->d_name); + printf("\tDevice state: %lu\n", vol->dev->d_state); + printf("\tVolume Name: %s\n", vol->vol_name); + printf("\tVolume State: %lu\n", vol->state); + printf("\tVolume Flags: 0x%04x", (int)vol->flags); + if (vol->flags & VOLUME_IS_DIRTY) + printf(" DIRTY"); + if (vol->flags & VOLUME_MODIFIED_BY_CHKDSK) + printf(" MODIFIED_BY_CHKDSK"); + printf("\n"); + printf("\tVolume Version: %u.%u\n", vol->major_ver, vol->minor_ver); + printf("\tSector Size: %hu\n", vol->sector_size); + printf("\tCluster Size: %u\n", (unsigned int)vol->cluster_size); + printf("\tIndex Block Size: %u\n", (unsigned int)vol->indx_record_size); + printf("\tVolume Size in Clusters: %lld\n", + (long long)vol->nr_clusters); + + printf("MFT Information \n"); + printf("\tMFT Record Size: %u\n", (unsigned int)vol->mft_record_size); + printf("\tMFT Zone Multiplier: %u\n", vol->mft_zone_multiplier); + printf("\tMFT Data Position: %lld\n", (long long)vol->mft_data_pos); + printf("\tMFT Zone Start: %lld\n", (long long)vol->mft_zone_start); + printf("\tMFT Zone End: %lld\n", (long long)vol->mft_zone_end); + printf("\tMFT Zone Position: %lld\n", (long long)vol->mft_zone_pos); + printf("\tCurrent Position in First Data Zone: %lld\n", + (long long)vol->data1_zone_pos); + printf("\tCurrent Position in Second Data Zone: %lld\n", + (long long)vol->data2_zone_pos); + printf("\tAllocated clusters %lld (%2.1lf%%)\n", + (long long)vol->mft_na->allocated_size + >> vol->cluster_size_bits, + 100.0*(vol->mft_na->allocated_size + >> vol->cluster_size_bits) + / vol->nr_clusters); + printf("\tLCN of Data Attribute for FILE_MFT: %lld\n", + (long long)vol->mft_lcn); + printf("\tFILE_MFTMirr Size: %d\n", vol->mftmirr_size); + printf("\tLCN of Data Attribute for File_MFTMirr: %lld\n", + (long long)vol->mftmirr_lcn); + printf("\tSize of Attribute Definition Table: %d\n", + (int)vol->attrdef_len); + printf("\tNumber of Attached Extent Inodes: %d\n", + (int)vol->mft_ni->nr_extents); + + printf("FILE_Bitmap Information \n"); + printf("\tFILE_Bitmap MFT Record Number: %llu\n", + (unsigned long long)vol->lcnbmp_ni->mft_no); + printf("\tState of FILE_Bitmap Inode: %lu\n", vol->lcnbmp_ni->state); + printf("\tLength of Attribute List: %u\n", + (unsigned int)vol->lcnbmp_ni->attr_list_size); + /* JPA printf("\tAttribute List: %s\n", vol->lcnbmp_ni->attr_list); */ + printf("\tNumber of Attached Extent Inodes: %d\n", + (int)vol->lcnbmp_ni->nr_extents); + /* FIXME: need to add code for the union if nr_extens != 0, but + i dont know if it will ever != 0 with FILE_Bitmap */ + + printf("FILE_Bitmap Data Attribute Information\n"); + printf("\tDecompressed Runlist: not done yet\n"); + printf("\tBase Inode: %llu\n", + (unsigned long long)vol->lcnbmp_na->ni->mft_no); + printf("\tAttribute Types: not done yet\n"); + //printf("\tAttribute Name: %s\n", vol->lcnbmp_na->name); + printf("\tAttribute Name Length: %u\n", + (unsigned int)vol->lcnbmp_na->name_len); + printf("\tAttribute State: %lu\n", vol->lcnbmp_na->state); + printf("\tAttribute Allocated Size: %lld\n", + (long long)vol->lcnbmp_na->allocated_size); + printf("\tAttribute Data Size: %lld\n", + (long long)vol->lcnbmp_na->data_size); + printf("\tAttribute Initialized Size: %lld\n", + (long long)vol->lcnbmp_na->initialized_size); + printf("\tAttribute Compressed Size: %lld\n", + (long long)vol->lcnbmp_na->compressed_size); + printf("\tCompression Block Size: %u\n", + (unsigned int)vol->lcnbmp_na->compression_block_size); + printf("\tCompression Block Size Bits: %u\n", + vol->lcnbmp_na->compression_block_size_bits); + printf("\tCompression Block Clusters: %u\n", + vol->lcnbmp_na->compression_block_clusters); + if (!ntfs_volume_get_free_space(vol)) + printf("\tFree Clusters: %lld (%2.1lf%%)\n", + (long long)vol->free_clusters, + 100.0*vol->free_clusters + /(double)vol->nr_clusters); + + //TODO: Still need to add a few more attributes +} + +/** + * ntfs_dump_flags - Dump flags for STANDARD_INFORMATION and FILE_NAME. + * @type: dump flags for this attribute type + * @flags: flags for dumping + */ +static void ntfs_dump_flags(const char *indent, ATTR_TYPES type, le32 flags) +{ + const le32 original_flags = flags; + + printf("%sFile attributes:\t", indent); + if (flags & FILE_ATTR_READONLY) { + printf(" READONLY"); + flags &= ~FILE_ATTR_READONLY; + } + if (flags & FILE_ATTR_HIDDEN) { + printf(" HIDDEN"); + flags &= ~FILE_ATTR_HIDDEN; + } + if (flags & FILE_ATTR_SYSTEM) { + printf(" SYSTEM"); + flags &= ~FILE_ATTR_SYSTEM; + } + if (flags & FILE_ATTR_DIRECTORY) { + printf(" DIRECTORY"); + flags &= ~FILE_ATTR_DIRECTORY; + } + if (flags & FILE_ATTR_ARCHIVE) { + printf(" ARCHIVE"); + flags &= ~FILE_ATTR_ARCHIVE; + } + if (flags & FILE_ATTR_DEVICE) { + printf(" DEVICE"); + flags &= ~FILE_ATTR_DEVICE; + } + if (flags & FILE_ATTR_NORMAL) { + printf(" NORMAL"); + flags &= ~FILE_ATTR_NORMAL; + } + if (flags & FILE_ATTR_TEMPORARY) { + printf(" TEMPORARY"); + flags &= ~FILE_ATTR_TEMPORARY; + } + if (flags & FILE_ATTR_SPARSE_FILE) { + printf(" SPARSE_FILE"); + flags &= ~FILE_ATTR_SPARSE_FILE; + } + if (flags & FILE_ATTR_REPARSE_POINT) { + printf(" REPARSE_POINT"); + flags &= ~FILE_ATTR_REPARSE_POINT; + } + if (flags & FILE_ATTR_COMPRESSED) { + printf(" COMPRESSED"); + flags &= ~FILE_ATTR_COMPRESSED; + } + if (flags & FILE_ATTR_OFFLINE) { + printf(" OFFLINE"); + flags &= ~FILE_ATTR_OFFLINE; + } + if (flags & FILE_ATTR_NOT_CONTENT_INDEXED) { + printf(" NOT_CONTENT_INDEXED"); + flags &= ~FILE_ATTR_NOT_CONTENT_INDEXED; + } + if (flags & FILE_ATTR_ENCRYPTED) { + printf(" ENCRYPTED"); + flags &= ~FILE_ATTR_ENCRYPTED; + } + /* We know that FILE_ATTR_I30_INDEX_PRESENT only exists on $FILE_NAME, + and in case we are wrong, let it appear as UNKNOWN */ + if (type == AT_FILE_NAME) { + if (flags & FILE_ATTR_I30_INDEX_PRESENT) { + printf(" I30_INDEX"); + flags &= ~FILE_ATTR_I30_INDEX_PRESENT; + } + } + if (flags & FILE_ATTR_VIEW_INDEX_PRESENT) { + printf(" VIEW_INDEX"); + flags &= ~FILE_ATTR_VIEW_INDEX_PRESENT; + } + if (flags) + printf(" UNKNOWN: 0x%08x", (unsigned int)le32_to_cpu(flags)); + /* Print all the flags in hex. */ + printf(" (0x%08x)\n", (unsigned)le32_to_cpu(original_flags)); +} + +/** + * ntfs_dump_namespace + */ +static void ntfs_dump_namespace(const char *indent, u8 file_name_type) +{ + const char *mbs_file_type; + + /* name space */ + switch (file_name_type) { + case FILE_NAME_POSIX: + mbs_file_type = "POSIX"; + break; + case FILE_NAME_WIN32: + mbs_file_type = "Win32"; + break; + case FILE_NAME_DOS: + mbs_file_type = "DOS"; + break; + case FILE_NAME_WIN32_AND_DOS: + mbs_file_type = "Win32 & DOS"; + break; + default: + mbs_file_type = "(unknown)"; + } + printf("%sNamespace:\t\t %s\n", indent, mbs_file_type); +} + +/* *************** functions for dumping attributes ******************** */ +/** + * ntfs_dump_standard_information + */ +static void ntfs_dump_attr_standard_information(ATTR_RECORD *attr) +{ + STANDARD_INFORMATION *standard_attr = NULL; + u32 value_length; + + standard_attr = (STANDARD_INFORMATION*)((char *)attr + + le16_to_cpu(attr->value_offset)); + + /* time conversion stuff */ + if (!opts.notime) { + char *ntfs_time_str = NULL; + + ntfs_time_str = ntfsinfo_time_to_str(standard_attr->creation_time); + printf("\tFile Creation Time:\t %s",ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str( + standard_attr->last_data_change_time); + printf("\tFile Altered Time:\t %s",ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str( + standard_attr->last_mft_change_time); + printf("\tMFT Changed Time:\t %s",ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str(standard_attr->last_access_time); + printf("\tLast Accessed Time:\t %s",ntfs_time_str); + } + ntfs_dump_flags("\t", attr->type, standard_attr->file_attributes); + + value_length = le32_to_cpu(attr->value_length); + if (value_length == 48) { + /* Only 12 reserved bytes here */ + } else if (value_length == 72) { + printf("\tMaximum versions:\t %u \n", (unsigned int) + le32_to_cpu(standard_attr->maximum_versions)); + printf("\tVersion number:\t\t %u \n", (unsigned int) + le32_to_cpu(standard_attr->version_number)); + printf("\tClass ID:\t\t %u \n", + (unsigned int)le32_to_cpu(standard_attr->class_id)); + printf("\tUser ID:\t\t %u (0x%x)\n", + (unsigned int)le32_to_cpu(standard_attr->owner_id), + (unsigned int)le32_to_cpu(standard_attr->owner_id)); + printf("\tSecurity ID:\t\t %u (0x%x)\n", + (unsigned int)le32_to_cpu(standard_attr->security_id), + (unsigned int)le32_to_cpu(standard_attr->security_id)); + printf("\tQuota charged:\t\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(standard_attr->quota_charged), + (unsigned long long) + le64_to_cpu(standard_attr->quota_charged)); + printf("\tUpdate Sequence Number:\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(standard_attr->usn), + (unsigned long long) + le64_to_cpu(standard_attr->usn)); + } else { + printf("\tSize of STANDARD_INFORMATION is %u (0x%x). It " + "should be either 72 or 48, something is " + "wrong...\n", (unsigned int)value_length, + (unsigned)value_length); + } +} + +static void ntfs_dump_bytes(u8 *buf, int start, int stop) +{ + int i; + + for (i = start; i < stop; i++) { + printf("%02x ", buf[i]); + } +} + +/** + * ntfs_dump_attr_list() + */ +static void ntfs_dump_attr_list(ATTR_RECORD *attr, ntfs_volume *vol) +{ + ATTR_LIST_ENTRY *entry; + u8 *value; + s64 l; + + if (!opts.verbose) + return; + + l = ntfs_get_attribute_value_length(attr); + if (!l) { + ntfs_log_perror("ntfs_get_attribute_value_length failed"); + return; + } + value = ntfs_malloc(l); + if (!value) + return; + + l = ntfs_get_attribute_value(vol, attr, value); + if (!l) { + ntfs_log_perror("ntfs_get_attribute_value failed"); + free(value); + return; + } + printf("\tDumping attribute list:"); + entry = (ATTR_LIST_ENTRY *) value; + for (;(u8 *)entry < (u8 *) value + l; entry = (ATTR_LIST_ENTRY *) + ((u8 *) entry + le16_to_cpu(entry->length))) { + printf("\n"); + printf("\t\tAttribute type:\t0x%x\n", + (unsigned int)le32_to_cpu(entry->type)); + printf("\t\tRecord length:\t%u (0x%x)\n", + (unsigned)le16_to_cpu(entry->length), + (unsigned)le16_to_cpu(entry->length)); + printf("\t\tName length:\t%u (0x%x)\n", + (unsigned)entry->name_length, + (unsigned)entry->name_length); + printf("\t\tName offset:\t%u (0x%x)\n", + (unsigned)entry->name_offset, + (unsigned)entry->name_offset); + printf("\t\tStarting VCN:\t%lld (0x%llx)\n", + (long long)sle64_to_cpu(entry->lowest_vcn), + (unsigned long long) + sle64_to_cpu(entry->lowest_vcn)); + printf("\t\tMFT reference:\t%lld (0x%llx)\n", + (unsigned long long) + MREF_LE(entry->mft_reference), + (unsigned long long) + MREF_LE(entry->mft_reference)); + printf("\t\tInstance:\t%u (0x%x)\n", + (unsigned)le16_to_cpu(entry->instance), + (unsigned)le16_to_cpu(entry->instance)); + printf("\t\tName:\t\t"); + if (entry->name_length) { + char *name = NULL; + int name_size; + + name_size = ntfs_ucstombs(entry->name, + entry->name_length, &name, 0); + + if (name_size > 0) { + printf("%s\n", name); + free(name); + } else + ntfs_log_perror("ntfs_ucstombs failed"); + } else + printf("unnamed\n"); + printf("\t\tPadding:\t"); + ntfs_dump_bytes((u8 *)entry, entry->name_offset + + sizeof(ntfschar) * entry->name_length, + le16_to_cpu(entry->length)); + printf("\n"); + } + free(value); + printf("\tEnd of attribute list reached.\n"); +} + +/** + * ntfs_dump_filename() + */ +static void ntfs_dump_filename(const char *indent, + FILE_NAME_ATTR *file_name_attr) +{ + printf("%sParent directory:\t %lld (0x%llx)\n", indent, + (long long)MREF_LE(file_name_attr->parent_directory), + (long long)MREF_LE(file_name_attr->parent_directory)); + /* time stuff */ + if (!opts.notime) { + char *ntfs_time_str; + + ntfs_time_str = ntfsinfo_time_to_str( + file_name_attr->creation_time); + printf("%sFile Creation Time:\t %s", indent, ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str( + file_name_attr->last_data_change_time); + printf("%sFile Altered Time:\t %s", indent, ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str( + file_name_attr->last_mft_change_time); + printf("%sMFT Changed Time:\t %s", indent, ntfs_time_str); + + ntfs_time_str = ntfsinfo_time_to_str( + file_name_attr->last_access_time); + printf("%sLast Accessed Time:\t %s", indent, ntfs_time_str); + } + /* other basic stuff about the file */ + printf("%sAllocated Size:\t\t %lld (0x%llx)\n", indent, (long long) + sle64_to_cpu(file_name_attr->allocated_size), + (unsigned long long) + sle64_to_cpu(file_name_attr->allocated_size)); + printf("%sData Size:\t\t %lld (0x%llx)\n", indent, + (long long)sle64_to_cpu(file_name_attr->data_size), + (unsigned long long) + sle64_to_cpu(file_name_attr->data_size)); + printf("%sFilename Length:\t %d (0x%x)\n", indent, + (unsigned)file_name_attr->file_name_length, + (unsigned)file_name_attr->file_name_length); + ntfs_dump_flags(indent, AT_FILE_NAME, file_name_attr->file_attributes); + if (file_name_attr->file_attributes & FILE_ATTR_REPARSE_POINT && + file_name_attr->reparse_point_tag) + printf("%sReparse point tag:\t 0x%x\n", indent, (unsigned) + le32_to_cpu(file_name_attr->reparse_point_tag)); + else if (file_name_attr->reparse_point_tag) { + printf("%sEA Length:\t\t %d (0x%x)\n", indent, (unsigned) + le16_to_cpu(file_name_attr->packed_ea_size), + (unsigned) + le16_to_cpu(file_name_attr->packed_ea_size)); + if (file_name_attr->reserved) + printf("%sReserved:\t\t %d (0x%x)\n", indent, + (unsigned) + le16_to_cpu(file_name_attr->reserved), + (unsigned) + le16_to_cpu(file_name_attr->reserved)); + } + /* The filename. */ + ntfs_dump_namespace(indent, file_name_attr->file_name_type); + if (file_name_attr->file_name_length > 0) { + /* but first we need to convert the little endian unicode string + into a printable format */ + char *mbs_file_name = NULL; + int mbs_file_name_size; + + mbs_file_name_size = ntfs_ucstombs(file_name_attr->file_name, + file_name_attr->file_name_length,&mbs_file_name,0); + + if (mbs_file_name_size>0) { + printf("%sFilename:\t\t '%s'\n", indent, mbs_file_name); + free(mbs_file_name); + } else { + /* an error occurred, errno holds the reason - notify the user */ + ntfs_log_perror("ntfsinfo error: could not parse file name"); + } + } else { + printf("%sFile Name:\t\t unnamed?!?\n", indent); + } +} + +/** + * ntfs_dump_attr_file_name() + */ +static void ntfs_dump_attr_file_name(ATTR_RECORD *attr) +{ + ntfs_dump_filename("\t", (FILE_NAME_ATTR*)((u8*)attr + + le16_to_cpu(attr->value_offset))); +} + +/** + * ntfs_dump_object_id + * + * dump the $OBJECT_ID attribute - not present on all systems + */ +static void ntfs_dump_attr_object_id(ATTR_RECORD *attr,ntfs_volume *vol) +{ + OBJECT_ID_ATTR *obj_id_attr = NULL; + + obj_id_attr = (OBJECT_ID_ATTR *)((u8*)attr + + le16_to_cpu(attr->value_offset)); + + if (vol->major_ver >= 3.0) { + u32 value_length; + char printable_GUID[37]; + + value_length = le32_to_cpu(attr->value_length); + + /* Object ID is mandatory. */ + ntfs_guid_to_mbs(&obj_id_attr->object_id, printable_GUID); + printf("\tObject ID:\t\t %s\n", printable_GUID); + + /* Dump Birth Volume ID. */ + if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero( + &obj_id_attr->birth_volume_id)) { + ntfs_guid_to_mbs(&obj_id_attr->birth_volume_id, + printable_GUID); + printf("\tBirth Volume ID:\t\t %s\n", printable_GUID); + } else + printf("\tBirth Volume ID:\t missing\n"); + + /* Dumping Birth Object ID */ + if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero( + &obj_id_attr->birth_object_id)) { + ntfs_guid_to_mbs(&obj_id_attr->birth_object_id, + printable_GUID); + printf("\tBirth Object ID:\t\t %s\n", printable_GUID); + } else + printf("\tBirth Object ID:\t missing\n"); + + /* Dumping Domain_id - reserved for now */ + if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero( + &obj_id_attr->domain_id)) { + ntfs_guid_to_mbs(&obj_id_attr->domain_id, + printable_GUID); + printf("\tDomain ID:\t\t\t %s\n", printable_GUID); + } else + printf("\tDomain ID:\t\t missing\n"); + } else + printf("\t$OBJECT_ID not present. Only NTFS versions > 3.0\n" + "\thave $OBJECT_ID. Your version of NTFS is %d.\n", + vol->major_ver); +} + +/** + * ntfs_dump_acl + * + * given an acl, print it in a beautiful & lovely way. + */ +static void ntfs_dump_acl(const char *prefix, ACL *acl) +{ + unsigned int i; + u16 ace_count; + ACCESS_ALLOWED_ACE *ace; + + printf("%sRevision\t %u\n", prefix, acl->revision); + + /* + * Do not recalculate le16_to_cpu every iteration (minor speedup on + * big-endian machines. + */ + ace_count = le16_to_cpu(acl->ace_count); + + /* initialize 'ace' to the first ace (if any) */ + ace = (ACCESS_ALLOWED_ACE *)((char *)acl + 8); + + /* iterate through ACE's */ + for (i = 1; i <= ace_count; i++) { + const char *ace_type; + char *sid; + + /* set ace_type. */ + switch (ace->type) { + case ACCESS_ALLOWED_ACE_TYPE: + ace_type = "allow"; + break; + case ACCESS_DENIED_ACE_TYPE: + ace_type = "deny"; + break; + case SYSTEM_AUDIT_ACE_TYPE: + ace_type = "audit"; + break; + default: + ace_type = "unknown"; + break; + } + + printf("%sACE:\t\t type:%s flags:0x%x access:0x%x\n", prefix, + ace_type, (unsigned int)ace->flags, + (unsigned int)le32_to_cpu(ace->mask)); + /* get a SID string */ + sid = ntfs_sid_to_mbs(&ace->sid, NULL, 0); + printf("%s\t\t SID: %s\n", prefix, sid); + free(sid); + + /* proceed to next ACE */ + ace = (ACCESS_ALLOWED_ACE *)(((char *)ace) + + le16_to_cpu(ace->size)); + } +} + + +static void ntfs_dump_security_descriptor(SECURITY_DESCRIPTOR_ATTR *sec_desc, + const char *indent) +{ + char *sid; + + printf("%s\tRevision:\t\t %u\n", indent, sec_desc->revision); + + /* TODO: parse the flags */ + printf("%s\tControl:\t\t 0x%04x\n", indent, + le16_to_cpu(sec_desc->control)); + + if (~sec_desc->control & SE_SELF_RELATIVE) { + SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)sec_desc; + + printf("%s\tOwner SID pointer:\t %p\n", indent, sd->owner); + printf("%s\tGroup SID pointer:\t %p\n", indent, sd->group); + printf("%s\tSACL pointer:\t\t %p\n", indent, sd->sacl); + printf("%s\tDACL pointer:\t\t %p\n", indent, sd->dacl); + + return; + } + + if (sec_desc->owner) { + sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc + + le32_to_cpu(sec_desc->owner)), NULL, 0); + printf("%s\tOwner SID:\t\t %s\n", indent, sid); + free(sid); + } else + printf("%s\tOwner SID:\t\t missing\n", indent); + + if (sec_desc->group) { + sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc + + le32_to_cpu(sec_desc->group)), NULL, 0); + printf("%s\tGroup SID:\t\t %s\n", indent, sid); + free(sid); + } else + printf("%s\tGroup SID:\t\t missing\n", indent); + + printf("%s\tSystem ACL:\t\t ", indent); + if (sec_desc->control & SE_SACL_PRESENT) { + if (sec_desc->control & SE_SACL_DEFAULTED) { + printf("defaulted"); + } + printf("\n"); + ntfs_dump_acl(indent ? "\t\t\t" : "\t\t", + (ACL *)((char *)sec_desc + + le32_to_cpu(sec_desc->sacl))); + } else { + printf("missing\n"); + } + + printf("%s\tDiscretionary ACL:\t ", indent); + if (sec_desc->control & SE_DACL_PRESENT) { + if (sec_desc->control & SE_SACL_DEFAULTED) { + printf("defaulted"); + } + printf("\n"); + ntfs_dump_acl(indent ? "\t\t\t" : "\t\t", + (ACL *)((char *)sec_desc + + le32_to_cpu(sec_desc->dacl))); + } else { + printf("missing\n"); + } +} + +/** + * ntfs_dump_security_descriptor() + * + * dump the security information about the file + */ +static void ntfs_dump_attr_security_descriptor(ATTR_RECORD *attr, ntfs_volume *vol) +{ + SECURITY_DESCRIPTOR_ATTR *sec_desc_attr; + + if (attr->non_resident) { + /* FIXME: We don't handle fragmented mapping pairs case. */ + runlist *rl = ntfs_mapping_pairs_decompress(vol, attr, NULL); + if (rl) { + s64 data_size, bytes_read; + + data_size = sle64_to_cpu(attr->data_size); + sec_desc_attr = ntfs_malloc(data_size); + if (!sec_desc_attr) { + free(rl); + return; + } + bytes_read = ntfs_rl_pread(vol, rl, 0, + data_size, sec_desc_attr); + if (bytes_read != data_size) { + ntfs_log_error("ntfsinfo error: could not " + "read security descriptor\n"); + free(rl); + free(sec_desc_attr); + return; + } + free(rl); + } else { + ntfs_log_error("ntfsinfo error: could not " + "decompress runlist\n"); + return; + } + } else { + sec_desc_attr = (SECURITY_DESCRIPTOR_ATTR *)((u8*)attr + + le16_to_cpu(attr->value_offset)); + } + + ntfs_dump_security_descriptor(sec_desc_attr, ""); + + if (attr->non_resident) + free(sec_desc_attr); +} + +/** + * ntfs_dump_volume_name() + * + * dump the name of the volume the inode belongs to + */ +static void ntfs_dump_attr_volume_name(ATTR_RECORD *attr) +{ + ntfschar *ucs_vol_name = NULL; + + if (le32_to_cpu(attr->value_length) > 0) { + char *mbs_vol_name = NULL; + int mbs_vol_name_size; + /* calculate volume name position */ + ucs_vol_name = (ntfschar*)((u8*)attr + + le16_to_cpu(attr->value_offset)); + /* convert the name to current locale multibyte sequence */ + mbs_vol_name_size = ntfs_ucstombs(ucs_vol_name, + le32_to_cpu(attr->value_length) / + sizeof(ntfschar), &mbs_vol_name, 0); + + if (mbs_vol_name_size>0) { + /* output the converted name. */ + printf("\tVolume Name:\t\t '%s'\n", mbs_vol_name); + free(mbs_vol_name); + } else + ntfs_log_perror("ntfsinfo error: could not parse " + "volume name"); + } else + printf("\tVolume Name:\t\t unnamed\n"); +} + +/** + * ntfs_dump_volume_information() + * + * dump the information for the volume the inode belongs to + * + */ +static void ntfs_dump_attr_volume_information(ATTR_RECORD *attr) +{ + VOLUME_INFORMATION *vol_information = NULL; + + vol_information = (VOLUME_INFORMATION*)((char *)attr+ + le16_to_cpu(attr->value_offset)); + + printf("\tVolume Version:\t\t %d.%d\n", vol_information->major_ver, + vol_information->minor_ver); + printf("\tVolume Flags:\t\t "); + if (vol_information->flags & VOLUME_IS_DIRTY) + printf("DIRTY "); + if (vol_information->flags & VOLUME_RESIZE_LOG_FILE) + printf("RESIZE_LOG "); + if (vol_information->flags & VOLUME_UPGRADE_ON_MOUNT) + printf("UPG_ON_MOUNT "); + if (vol_information->flags & VOLUME_MOUNTED_ON_NT4) + printf("MOUNTED_NT4 "); + if (vol_information->flags & VOLUME_DELETE_USN_UNDERWAY) + printf("DEL_USN "); + if (vol_information->flags & VOLUME_REPAIR_OBJECT_ID) + printf("REPAIR_OBJID "); + if (vol_information->flags & VOLUME_CHKDSK_UNDERWAY) + printf("CHKDSK_UNDERWAY "); + if (vol_information->flags & VOLUME_MODIFIED_BY_CHKDSK) + printf("MOD_BY_CHKDSK "); + if (vol_information->flags & VOLUME_FLAGS_MASK) { + printf("(0x%04x)\n", + (unsigned)le16_to_cpu(vol_information->flags)); + } else + printf("none set (0x0000)\n"); + if (vol_information->flags & (~VOLUME_FLAGS_MASK)) + printf("\t\t\t\t Unknown Flags: 0x%04x\n", + le16_to_cpu(vol_information->flags & + (~VOLUME_FLAGS_MASK))); +} + +static ntfschar NTFS_DATA_SDS[5] = { const_cpu_to_le16('$'), + const_cpu_to_le16('S'), const_cpu_to_le16('D'), + const_cpu_to_le16('S'), const_cpu_to_le16('\0') }; + +static void ntfs_dump_sds_entry(SECURITY_DESCRIPTOR_HEADER *sds) +{ + SECURITY_DESCRIPTOR_RELATIVE *sd; + + ntfs_log_verbose("\n"); + ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", + (unsigned)le32_to_cpu(sds->hash)); + ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(sds->security_id), + (unsigned)le32_to_cpu(sds->security_id)); + ntfs_log_verbose("\t\tOffset:\t\t\t %llu (0x%llx)\n", + (unsigned long long)le64_to_cpu(sds->offset), + (unsigned long long)le64_to_cpu(sds->offset)); + ntfs_log_verbose("\t\tLength:\t\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(sds->length), + (unsigned)le32_to_cpu(sds->length)); + + sd = (SECURITY_DESCRIPTOR_RELATIVE *)((char *)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + + ntfs_dump_security_descriptor(sd, "\t"); +} + +static void ntfs_dump_sds(ATTR_RECORD *attr, ntfs_inode *ni) +{ + SECURITY_DESCRIPTOR_HEADER *sds, *sd; + ntfschar *name; + int name_len; + s64 data_size; + u64 inode; + + inode = ni->mft_no; + if (ni->nr_extents < 0) + inode = ni->base_ni->mft_no; + if (FILE_Secure != inode) + return; + + name_len = attr->name_length; + if (!name_len) + return; + + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); + if (!ntfs_names_are_equal(NTFS_DATA_SDS, sizeof(NTFS_DATA_SDS) / 2 - 1, + name, name_len, CASE_SENSITIVE, NULL, 0)) + return; + + sd = sds = ntfs_attr_readall(ni, AT_DATA, name, name_len, &data_size); + if (!sd) { + ntfs_log_perror("Failed to read $SDS attribute"); + return; + } + /* + * FIXME: The right way is based on the indexes, so we couldn't + * miss real entries. For now, dump until it makes sense. + */ + while (sd->length && sd->hash && + le64_to_cpu(sd->offset) < (u64)data_size && + le32_to_cpu(sd->length) < (u64)data_size && + le64_to_cpu(sd->offset) + + le32_to_cpu(sd->length) < (u64)data_size) { + ntfs_dump_sds_entry(sd); + sd = (SECURITY_DESCRIPTOR_HEADER *)((char*)sd + + ((le32_to_cpu(sd->length) + 15) & ~15)); + } + free(sds); +} + +static const char *get_attribute_type_name(le32 type) +{ + switch (type) { + case AT_UNUSED: return "$UNUSED"; + case AT_STANDARD_INFORMATION: return "$STANDARD_INFORMATION"; + case AT_ATTRIBUTE_LIST: return "$ATTRIBUTE_LIST"; + case AT_FILE_NAME: return "$FILE_NAME"; + case AT_OBJECT_ID: return "$OBJECT_ID"; + case AT_SECURITY_DESCRIPTOR: return "$SECURITY_DESCRIPTOR"; + case AT_VOLUME_NAME: return "$VOLUME_NAME"; + case AT_VOLUME_INFORMATION: return "$VOLUME_INFORMATION"; + case AT_DATA: return "$DATA"; + case AT_INDEX_ROOT: return "$INDEX_ROOT"; + case AT_INDEX_ALLOCATION: return "$INDEX_ALLOCATION"; + case AT_BITMAP: return "$BITMAP"; + case AT_REPARSE_POINT: return "$REPARSE_POINT"; + case AT_EA_INFORMATION: return "$EA_INFORMATION"; + case AT_EA: return "$EA"; + case AT_PROPERTY_SET: return "$PROPERTY_SET"; + case AT_LOGGED_UTILITY_STREAM: return "$LOGGED_UTILITY_STREAM"; + case AT_END: return "$END"; + } + + return "$UNKNOWN"; +} + +static const char * ntfs_dump_lcn(LCN lcn) +{ + switch (lcn) { + case LCN_HOLE: + return "<HOLE>\t"; + case LCN_RL_NOT_MAPPED: + return "<RL_NOT_MAPPED>"; + case LCN_ENOENT: + return "<ENOENT>\t"; + case LCN_EINVAL: + return "<EINVAL>\t"; + case LCN_EIO: + return "<EIO>\t"; + default: + ntfs_log_error("Invalid LCN value %llx passed to " + "ntfs_dump_lcn().\n", (long long)lcn); + return "???\t"; + } +} + +static void ntfs_dump_attribute_header(ntfs_attr_search_ctx *ctx, + ntfs_volume *vol, struct RUNCOUNT *runcount) +{ + ATTR_RECORD *a = ctx->attr; + + printf("Dumping attribute %s (0x%x) from mft record %lld (0x%llx)\n", + get_attribute_type_name(a->type), + (unsigned)le32_to_cpu(a->type), + (unsigned long long)ctx->ntfs_ino->mft_no, + (unsigned long long)ctx->ntfs_ino->mft_no); + + ntfs_log_verbose("\tAttribute length:\t %u (0x%x)\n", + (unsigned)le32_to_cpu(a->length), + (unsigned)le32_to_cpu(a->length)); + printf("\tResident: \t\t %s\n", a->non_resident ? "No" : "Yes"); + ntfs_log_verbose("\tName length:\t\t %u (0x%x)\n", + (unsigned)a->name_length, (unsigned)a->name_length); + ntfs_log_verbose("\tName offset:\t\t %u (0x%x)\n", + (unsigned)le16_to_cpu(a->name_offset), + (unsigned)le16_to_cpu(a->name_offset)); + + /* Dump the attribute (stream) name */ + if (a->name_length) { + char *attribute_name = NULL; + + attribute_name = ntfs_attr_get_name_mbs(a); + if (attribute_name) { + printf("\tAttribute name:\t\t '%s'\n", attribute_name); + free(attribute_name); + } else + ntfs_log_perror("Error: couldn't parse attribute name"); + } + + /* TODO: parse the flags */ + printf("\tAttribute flags:\t 0x%04x\n", + (unsigned)le16_to_cpu(a->flags)); + printf("\tAttribute instance:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(a->instance), + (unsigned)le16_to_cpu(a->instance)); + + /* Resident attribute */ + if (!a->non_resident) { + printf("\tData size:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(a->value_length), + (unsigned)le32_to_cpu(a->value_length)); + ntfs_log_verbose("\tData offset:\t\t %u (0x%x)\n", + (unsigned)le16_to_cpu(a->value_offset), + (unsigned)le16_to_cpu(a->value_offset)); + /* TODO: parse the flags */ + printf("\tResident flags:\t\t 0x%02x\n", + (unsigned)a->resident_flags); + ntfs_log_verbose("\tReservedR:\t\t %d (0x%x)\n", + (unsigned)a->reservedR, (unsigned)a->reservedR); + return; + } + + /* Non-resident attribute */ + ntfs_log_verbose("\tLowest VCN\t\t %lld (0x%llx)\n", + (long long)sle64_to_cpu(a->lowest_vcn), + (unsigned long long)sle64_to_cpu(a->lowest_vcn)); + ntfs_log_verbose("\tHighest VCN:\t\t %lld (0x%llx)\n", + (long long)sle64_to_cpu(a->highest_vcn), + (unsigned long long)sle64_to_cpu(a->highest_vcn)); + ntfs_log_verbose("\tMapping pairs offset:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(a->mapping_pairs_offset), + (unsigned)le16_to_cpu(a->mapping_pairs_offset)); + printf("\tCompression unit:\t %u (0x%x)\n", + (unsigned)a->compression_unit, + (unsigned)a->compression_unit); + /* TODO: dump the 5 reserved bytes here in verbose mode */ + + if (!a->lowest_vcn) { + printf("\tData size:\t\t %llu (0x%llx)\n", + (long long)sle64_to_cpu(a->data_size), + (unsigned long long)sle64_to_cpu(a->data_size)); + printf("\tAllocated size:\t\t %llu (0x%llx)\n", + (long long)sle64_to_cpu(a->allocated_size), + (unsigned long long) + sle64_to_cpu(a->allocated_size)); + printf("\tInitialized size:\t %llu (0x%llx)\n", + (long long)sle64_to_cpu(a->initialized_size), + (unsigned long long) + sle64_to_cpu(a->initialized_size)); + if (a->compression_unit || a->flags & ATTR_IS_COMPRESSED || + a->flags & ATTR_IS_SPARSE) + printf("\tCompressed size:\t %llu (0x%llx)\n", + (signed long long) + sle64_to_cpu(a->compressed_size), + (signed long long) + sle64_to_cpu(a->compressed_size)); + } + + if (opts.verbose) { + runlist *rl; + + rl = ntfs_mapping_pairs_decompress(vol, a, NULL); + if (rl) { + runlist *rlc = rl; + LCN next_lcn; + + next_lcn = LCN_HOLE; + // TODO: Switch this to properly aligned hex... + printf("\tRunlist:\tVCN\t\tLCN\t\tLength\n"); + runcount->fragments++; + while (rlc->length) { + runcount->runs++; + if (rlc->lcn >= 0) { + printf("\t\t\t0x%llx\t\t0x%llx\t\t" + "0x%llx\n", + (long long)rlc->vcn, + (long long)rlc->lcn, + (long long)rlc->length); + if ((next_lcn >= 0) + && (rlc->lcn != next_lcn)) + runcount->fragments++; + next_lcn = rlc->lcn + rlc->length; + } else + printf("\t\t\t0x%llx\t\t%s\t" + "0x%llx\n", + (long long)rlc->vcn, + ntfs_dump_lcn(rlc->lcn), + (long long)rlc->length); + rlc++; + } + free(rl); + } else + ntfs_log_error("Error: couldn't decompress runlist\n"); + } +} + +/** + * ntfs_dump_data_attr() + * + * dump some info about the data attribute if it's metadata + */ +static void ntfs_dump_attr_data(ATTR_RECORD *attr, ntfs_inode *ni) +{ + if (opts.verbose) + ntfs_dump_sds(attr, ni); +} + +typedef enum { + INDEX_ATTR_UNKNOWN, + INDEX_ATTR_DIRECTORY_I30, + INDEX_ATTR_SECURE_SII, + INDEX_ATTR_SECURE_SDH, + INDEX_ATTR_OBJID_O, + INDEX_ATTR_REPARSE_R, + INDEX_ATTR_QUOTA_O, + INDEX_ATTR_QUOTA_Q, +} INDEX_ATTR_TYPE; + +static void ntfs_dump_index_key(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) +{ + char *sid; + char printable_GUID[37]; + + switch (type) { + case INDEX_ATTR_SECURE_SII: + ntfs_log_verbose("\t\tKey security id:\t %u (0x%x)\n", + (unsigned) + le32_to_cpu(entry->key.sii.security_id), + (unsigned) + le32_to_cpu(entry->key.sii.security_id)); + break; + case INDEX_ATTR_SECURE_SDH: + ntfs_log_verbose("\t\tKey hash:\t\t 0x%08x\n", + (unsigned)le32_to_cpu(entry->key.sdh.hash)); + ntfs_log_verbose("\t\tKey security id:\t %u (0x%x)\n", + (unsigned) + le32_to_cpu(entry->key.sdh.security_id), + (unsigned) + le32_to_cpu(entry->key.sdh.security_id)); + break; + case INDEX_ATTR_OBJID_O: + ntfs_guid_to_mbs(&entry->key.object_id, printable_GUID); + ntfs_log_verbose("\t\tKey GUID:\t\t %s\n", printable_GUID); + break; + case INDEX_ATTR_REPARSE_R: + ntfs_log_verbose("\t\tKey reparse tag:\t 0x%08x\n", (unsigned) + le32_to_cpu(entry->key.reparse.reparse_tag)); + ntfs_log_verbose("\t\tKey file id:\t\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(entry->key.reparse.file_id), + (unsigned long long) + le64_to_cpu(entry->key.reparse.file_id)); + break; + case INDEX_ATTR_QUOTA_O: + sid = ntfs_sid_to_mbs(&entry->key.sid, NULL, 0); + ntfs_log_verbose("\t\tKey SID:\t\t %s\n", sid); + free(sid); + break; + case INDEX_ATTR_QUOTA_Q: + ntfs_log_verbose("\t\tKey owner id:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(entry->key.owner_id), + (unsigned)le32_to_cpu(entry->key.owner_id)); + break; + default: + ntfs_log_verbose("\t\tIndex attr type is UNKNOWN: \t 0x%08x\n", + (unsigned)type); + break; + } +} + +typedef union { + SII_INDEX_DATA sii; /* $SII index data in $Secure */ + SDH_INDEX_DATA sdh; /* $SDH index data in $Secure */ + QUOTA_O_INDEX_DATA quota_o; /* $O index data in $Quota */ + QUOTA_CONTROL_ENTRY quota_q; /* $Q index data in $Quota */ +} __attribute__((__packed__)) INDEX_ENTRY_DATA; + +static void ntfs_dump_index_data(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) +{ + INDEX_ENTRY_DATA *data; + + data = (INDEX_ENTRY_DATA *)((u8 *)entry + + le16_to_cpu(entry->data_offset)); + + switch (type) { + case INDEX_ATTR_SECURE_SII: + ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", + (unsigned)le32_to_cpu(data->sii.hash)); + ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->sii.security_id), + (unsigned)le32_to_cpu(data->sii.security_id)); + ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(data->sii.offset), + (unsigned long long) + le64_to_cpu(data->sii.offset)); + ntfs_log_verbose("\t\tLength in $SDS:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->sii.length), + (unsigned)le32_to_cpu(data->sii.length)); + break; + case INDEX_ATTR_SECURE_SDH: + ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", + (unsigned)le32_to_cpu(data->sdh.hash)); + ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->sdh.security_id), + (unsigned)le32_to_cpu(data->sdh.security_id)); + ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(data->sdh.offset), + (unsigned long long) + le64_to_cpu(data->sdh.offset)); + ntfs_log_verbose("\t\tLength in $SDS:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->sdh.length), + (unsigned)le32_to_cpu(data->sdh.length)); + ntfs_log_verbose("\t\tUnknown (padding):\t 0x%08x\n", + (unsigned)le32_to_cpu(data->sdh.reserved_II)); + break; + case INDEX_ATTR_OBJID_O: { + OBJ_ID_INDEX_DATA *object_id_data; + char printable_GUID[37]; + + object_id_data = (OBJ_ID_INDEX_DATA*)((u8*)entry + + le16_to_cpu(entry->data_offset)); + ntfs_log_verbose("\t\tMFT Number:\t\t 0x%llx\n", + (unsigned long long) + MREF_LE(object_id_data->mft_reference)); + ntfs_log_verbose("\t\tMFT Sequence Number:\t 0x%x\n", + (unsigned) + MSEQNO_LE(object_id_data->mft_reference)); + ntfs_guid_to_mbs(&object_id_data->birth_volume_id, + printable_GUID); + ntfs_log_verbose("\t\tBirth volume id GUID:\t %s\n", + printable_GUID); + ntfs_guid_to_mbs(&object_id_data->birth_object_id, + printable_GUID); + ntfs_log_verbose("\t\tBirth object id GUID:\t %s\n", + printable_GUID); + ntfs_guid_to_mbs(&object_id_data->domain_id, printable_GUID); + ntfs_log_verbose("\t\tDomain id GUID:\t\t %s\n", + printable_GUID); + } + break; + case INDEX_ATTR_REPARSE_R: + /* TODO */ + break; + case INDEX_ATTR_QUOTA_O: + ntfs_log_verbose("\t\tOwner id:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->quota_o.owner_id), + (unsigned)le32_to_cpu(data->quota_o.owner_id)); + ntfs_log_verbose("\t\tUnknown:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->quota_o.unknown), + (unsigned)le32_to_cpu(data->quota_o.unknown)); + break; + case INDEX_ATTR_QUOTA_Q: + ntfs_log_verbose("\t\tVersion:\t\t %u\n", + (unsigned)le32_to_cpu(data->quota_q.version)); + ntfs_log_verbose("\t\tQuota flags:\t\t 0x%08x\n", + (unsigned)le32_to_cpu(data->quota_q.flags)); + ntfs_log_verbose("\t\tBytes used:\t\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(data->quota_q.bytes_used), + (unsigned long long) + le64_to_cpu(data->quota_q.bytes_used)); + ntfs_log_verbose("\t\tLast changed:\t\t %s", + ntfsinfo_time_to_str( + data->quota_q.change_time)); + ntfs_log_verbose("\t\tThreshold:\t\t %lld (0x%llx)\n", + (unsigned long long) + sle64_to_cpu(data->quota_q.threshold), + (unsigned long long) + sle64_to_cpu(data->quota_q.threshold)); + ntfs_log_verbose("\t\tLimit:\t\t\t %lld (0x%llx)\n", + (unsigned long long) + sle64_to_cpu(data->quota_q.limit), + (unsigned long long) + sle64_to_cpu(data->quota_q.limit)); + ntfs_log_verbose("\t\tExceeded time:\t\t %lld (0x%llx)\n", + (unsigned long long) + sle64_to_cpu(data->quota_q.exceeded_time), + (unsigned long long) + sle64_to_cpu(data->quota_q.exceeded_time)); + if (le16_to_cpu(entry->data_length) > 48) { + char *sid; + sid = ntfs_sid_to_mbs(&data->quota_q.sid, NULL, 0); + ntfs_log_verbose("\t\tOwner SID:\t\t %s\n", sid); + free(sid); + } + break; + default: + ntfs_log_verbose("\t\tIndex attr type is UNKNOWN: \t 0x%08x\n", + (unsigned)type); + break; + } +} + +/** + * ntfs_dump_index_entries() + * + * dump sequence of index_entries and return number of entries dumped. + */ +static int ntfs_dump_index_entries(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) +{ + int numb_entries = 1; + while (1) { + if (!opts.verbose) { + if (entry->ie_flags & INDEX_ENTRY_END) + break; + entry = (INDEX_ENTRY *)((u8 *)entry + + le16_to_cpu(entry->length)); + numb_entries++; + continue; + } + ntfs_log_verbose("\t\tEntry length:\t\t %u (0x%x)\n", + (unsigned)le16_to_cpu(entry->length), + (unsigned)le16_to_cpu(entry->length)); + ntfs_log_verbose("\t\tKey length:\t\t %u (0x%x)\n", + (unsigned)le16_to_cpu(entry->key_length), + (unsigned)le16_to_cpu(entry->key_length)); + ntfs_log_verbose("\t\tIndex entry flags:\t 0x%02x\n", + (unsigned)le16_to_cpu(entry->ie_flags)); + + if (entry->ie_flags & INDEX_ENTRY_NODE) + ntfs_log_verbose("\t\tSubnode VCN:\t\t %lld (0x%llx)\n", + (long long)ntfs_ie_get_vcn(entry), + (long long)ntfs_ie_get_vcn(entry)); + if (entry->ie_flags & INDEX_ENTRY_END) + break; + + switch (type) { + case INDEX_ATTR_DIRECTORY_I30: + ntfs_log_verbose("\t\tFILE record number:\t %llu " + "(0x%llx)\n", (unsigned long long) + MREF_LE(entry->indexed_file), + (unsigned long long) + MREF_LE(entry->indexed_file)); + ntfs_dump_filename("\t\t", &entry->key.file_name); + break; + default: + ntfs_log_verbose("\t\tData offset:\t\t %u (0x%x)\n", + (unsigned) + le16_to_cpu(entry->data_offset), + (unsigned) + le16_to_cpu(entry->data_offset)); + ntfs_log_verbose("\t\tData length:\t\t %u (0x%x)\n", + (unsigned) + le16_to_cpu(entry->data_length), + (unsigned) + le16_to_cpu(entry->data_length)); + ntfs_dump_index_key(entry, type); + ntfs_log_verbose("\t\tKey Data:\n"); + ntfs_dump_index_data(entry, type); + break; + } + if (!entry->length) { + ntfs_log_verbose("\tWARNING: Corrupt index entry, " + "skipping the remainder of this index " + "block.\n"); + break; + } + entry = (INDEX_ENTRY*)((u8*)entry + le16_to_cpu(entry->length)); + numb_entries++; + ntfs_log_verbose("\n"); + } + ntfs_log_verbose("\tEnd of index block reached\n"); + return numb_entries; +} + +#define COMPARE_INDEX_NAMES(attr, name) \ + ntfs_names_are_equal((name), sizeof(name) / 2 - 1, \ + (ntfschar*)((char*)(attr) + le16_to_cpu((attr)->name_offset)), \ + (attr)->name_length, CASE_SENSITIVE, NULL, 0) + +static INDEX_ATTR_TYPE get_index_attr_type(ntfs_inode *ni, ATTR_RECORD *attr, + INDEX_ROOT *index_root) +{ + char file_name[64]; + + if (!attr->name_length) + return INDEX_ATTR_UNKNOWN; + + if (index_root->type) { + if (index_root->type == AT_FILE_NAME) + return INDEX_ATTR_DIRECTORY_I30; + else + /* weird, this should be illegal */ + ntfs_log_error("Unknown index attribute type: 0x%0X\n", + index_root->type); + return INDEX_ATTR_UNKNOWN; + } + + if (utils_is_metadata(ni) <= 0) + return INDEX_ATTR_UNKNOWN; + if (utils_inode_get_name(ni, file_name, sizeof(file_name)) <= 0) + return INDEX_ATTR_UNKNOWN; + + if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SDH)) + return INDEX_ATTR_SECURE_SDH; + else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SII)) + return INDEX_ATTR_SECURE_SII; + else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SII)) + return INDEX_ATTR_SECURE_SII; + else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_Q)) + return INDEX_ATTR_QUOTA_Q; + else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_R)) + return INDEX_ATTR_REPARSE_R; + else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_O)) { + if (!strcmp(file_name, "/$Extend/$Quota")) + return INDEX_ATTR_QUOTA_O; + else if (!strcmp(file_name, "/$Extend/$ObjId")) + return INDEX_ATTR_OBJID_O; + } + + return INDEX_ATTR_UNKNOWN; +} + +static void ntfs_dump_index_attr_type(INDEX_ATTR_TYPE type) +{ + if (type == INDEX_ATTR_DIRECTORY_I30) + printf("DIRECTORY_I30"); + else if (type == INDEX_ATTR_SECURE_SDH) + printf("SECURE_SDH"); + else if (type == INDEX_ATTR_SECURE_SII) + printf("SECURE_SII"); + else if (type == INDEX_ATTR_OBJID_O) + printf("OBJID_O"); + else if (type == INDEX_ATTR_QUOTA_O) + printf("QUOTA_O"); + else if (type == INDEX_ATTR_QUOTA_Q) + printf("QUOTA_Q"); + else if (type == INDEX_ATTR_REPARSE_R) + printf("REPARSE_R"); + else + printf("UNKNOWN"); + printf("\n"); +} + +static void ntfs_dump_index_header(const char *indent, INDEX_HEADER *idx) +{ + printf("%sEntries Offset:\t\t %u (0x%x)\n", indent, + (unsigned)le32_to_cpu(idx->entries_offset), + (unsigned)le32_to_cpu(idx->entries_offset)); + printf("%sIndex Size:\t\t %u (0x%x)\n", indent, + (unsigned)le32_to_cpu(idx->index_length), + (unsigned)le32_to_cpu(idx->index_length)); + printf("%sAllocated Size:\t\t %u (0x%x)\n", indent, + (unsigned)le32_to_cpu(idx->allocated_size), + (unsigned)le32_to_cpu(idx->allocated_size)); + printf("%sIndex header flags:\t 0x%02x\n", indent, idx->ih_flags); + + /* FIXME: there are 3 reserved bytes here */ +} + +/** + * ntfs_dump_attr_index_root() + * + * dump the index_root attribute + */ +static void ntfs_dump_attr_index_root(ATTR_RECORD *attr, ntfs_inode *ni) +{ + INDEX_ATTR_TYPE type; + INDEX_ROOT *index_root = NULL; + INDEX_ENTRY *entry; + + index_root = (INDEX_ROOT*)((u8*)attr + le16_to_cpu(attr->value_offset)); + + /* attr_type dumping */ + type = get_index_attr_type(ni, attr, index_root); + printf("\tIndexed Attr Type:\t "); + ntfs_dump_index_attr_type(type); + + /* collation rule dumping */ + printf("\tCollation Rule:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(index_root->collation_rule), + (unsigned)le32_to_cpu(index_root->collation_rule)); +/* COLLATION_BINARY, COLLATION_FILE_NAME, COLLATION_UNICODE_STRING, + COLLATION_NTOFS_ULONG, COLLATION_NTOFS_SID, + COLLATION_NTOFS_SECURITY_HASH, COLLATION_NTOFS_ULONGS */ + + printf("\tIndex Block Size:\t %u (0x%x)\n", + (unsigned)le32_to_cpu(index_root->index_block_size), + (unsigned)le32_to_cpu(index_root->index_block_size)); + if (le32_to_cpu(index_root->index_block_size) < ni->vol->cluster_size) + printf("\t512-byte Units Per Block:\t %u (0x%x)\n", + (unsigned)index_root->clusters_per_index_block, + (unsigned)index_root->clusters_per_index_block); + else + printf("\tClusters Per Block:\t %u (0x%x)\n", + (unsigned)index_root->clusters_per_index_block, + (unsigned)index_root->clusters_per_index_block); + + ntfs_dump_index_header("\t", &index_root->index); + + entry = (INDEX_ENTRY*)((u8*)index_root + + le32_to_cpu(index_root->index.entries_offset) + 0x10); + ntfs_log_verbose("\tDumping index root:\n"); + printf("\tIndex entries total:\t %d\n", + ntfs_dump_index_entries(entry, type)); +} + +static void ntfs_dump_usa_lsn(const char *indent, MFT_RECORD *mrec) +{ + printf("%sUpd. Seq. Array Off.:\t %u (0x%x)\n", indent, + (unsigned)le16_to_cpu(mrec->usa_ofs), + (unsigned)le16_to_cpu(mrec->usa_ofs)); + printf("%sUpd. Seq. Array Count:\t %u (0x%x)\n", indent, + (unsigned)le16_to_cpu(mrec->usa_count), + (unsigned)le16_to_cpu(mrec->usa_count)); + printf("%sUpd. Seq. Number:\t %u (0x%x)\n", indent, + (unsigned)le16_to_cpup((le16*)((u8*)mrec + + le16_to_cpu(mrec->usa_ofs))), + (unsigned)le16_to_cpup((le16*)((u8*)mrec + + le16_to_cpu(mrec->usa_ofs)))); + printf("%sLogFile Seq. Number:\t 0x%llx\n", indent, + (unsigned long long)sle64_to_cpu(mrec->lsn)); +} + + +static s32 ntfs_dump_index_block(INDEX_BLOCK *ib, INDEX_ATTR_TYPE type, + u32 ib_size) +{ + INDEX_ENTRY *entry; + + if (ntfs_mst_post_read_fixup((NTFS_RECORD*)ib, ib_size)) { + ntfs_log_perror("Damaged INDX record"); + return -1; + } + ntfs_log_verbose("\tDumping index block:\n"); + if (opts.verbose) + ntfs_dump_usa_lsn("\t\t", (MFT_RECORD*)ib); + + ntfs_log_verbose("\t\tNode VCN:\t\t %lld (0x%llx)\n", + (unsigned long long)sle64_to_cpu(ib->index_block_vcn), + (unsigned long long)sle64_to_cpu(ib->index_block_vcn)); + + entry = (INDEX_ENTRY*)((u8*)ib + + le32_to_cpu(ib->index.entries_offset) + 0x18); + + if (opts.verbose) { + ntfs_dump_index_header("\t\t", &ib->index); + printf("\n"); + } + + return ntfs_dump_index_entries(entry, type); +} + +/** + * ntfs_dump_attr_index_allocation() + * + * dump context of the index_allocation attribute + */ +static void ntfs_dump_attr_index_allocation(ATTR_RECORD *attr, ntfs_inode *ni) +{ + INDEX_ALLOCATION *allocation, *tmp_alloc; + INDEX_ROOT *ir; + INDEX_ATTR_TYPE type; + int total_entries = 0; + int total_indx_blocks = 0; + u8 *bitmap, *byte; + int bit; + ntfschar *name; + u32 name_len; + s64 data_size; + + ir = ntfs_index_root_get(ni, attr); + if (!ir) { + ntfs_log_perror("Failed to read $INDEX_ROOT attribute"); + return; + } + + type = get_index_attr_type(ni, attr, ir); + + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); + name_len = attr->name_length; + + byte = bitmap = ntfs_attr_readall(ni, AT_BITMAP, name, name_len, NULL); + if (!byte) { + ntfs_log_perror("Failed to read $BITMAP attribute"); + goto out_index_root; + } + + tmp_alloc = allocation = ntfs_attr_readall(ni, AT_INDEX_ALLOCATION, + name, name_len, &data_size); + if (!tmp_alloc) { + ntfs_log_perror("Failed to read $INDEX_ALLOCATION attribute"); + goto out_bitmap; + } + + bit = 0; + while ((u8 *)tmp_alloc < (u8 *)allocation + data_size) { + if (*byte & (1 << bit)) { + int entries; + + entries = ntfs_dump_index_block(tmp_alloc, type, + le32_to_cpu( + ir->index_block_size)); + if (entries != -1) { + total_entries += entries; + total_indx_blocks++; + ntfs_log_verbose("\tIndex entries:\t\t %d\n", + entries); + } + } + tmp_alloc = (INDEX_ALLOCATION *)((u8 *)tmp_alloc + + le32_to_cpu( + ir->index_block_size)); + bit++; + if (bit > 7) { + bit = 0; + byte++; + } + } + + printf("\tIndex entries total:\t %d\n", total_entries); + printf("\tINDX blocks total:\t %d\n", total_indx_blocks); + + free(allocation); +out_bitmap: + free(bitmap); +out_index_root: + free(ir); +} + +/** + * ntfs_dump_attr_bitmap() + * + * dump the bitmap attribute + */ +static void ntfs_dump_attr_bitmap(ATTR_RECORD *attr __attribute__((unused))) +{ + /* TODO */ +} + +/** + * ntfs_dump_attr_reparse_point() + * + * of ntfs 3.x dumps the reparse_point attribute + */ +static void ntfs_dump_attr_reparse_point(ATTR_RECORD *attr __attribute__((unused))) +{ + /* TODO */ +} + +/** + * ntfs_dump_attr_ea_information() + * + * dump the ea_information attribute + */ +static void ntfs_dump_attr_ea_information(ATTR_RECORD *attr) +{ + EA_INFORMATION *ea_info; + + ea_info = (EA_INFORMATION*)((u8*)attr + + le16_to_cpu(attr->value_offset)); + printf("\tPacked EA length:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(ea_info->ea_length), + (unsigned)le16_to_cpu(ea_info->ea_length)); + printf("\tNEED_EA count:\t\t %u (0x%x)\n", + (unsigned)le16_to_cpu(ea_info->need_ea_count), + (unsigned)le16_to_cpu(ea_info->need_ea_count)); + printf("\tUnpacked EA length:\t %u (0x%x)\n", + (unsigned)le32_to_cpu(ea_info->ea_query_length), + (unsigned)le32_to_cpu(ea_info->ea_query_length)); +} + +/** + * ntfs_dump_attr_ea() + * + * dump the ea attribute + */ +static void ntfs_dump_attr_ea(ATTR_RECORD *attr, ntfs_volume *vol) +{ + const EA_ATTR *ea; + const u8 *pvalue; + u8 *buf = NULL; + const le32 *pval; + int offset; + int cnt; + s64 data_size; + + if (attr->non_resident) { + runlist *rl; + + data_size = sle64_to_cpu(attr->data_size); + if (!opts.verbose) + return; + /* FIXME: We don't handle fragmented mapping pairs case. */ + rl = ntfs_mapping_pairs_decompress(vol, attr, NULL); + if (rl) { + s64 bytes_read; + + buf = ntfs_malloc(data_size); + if (!buf) { + free(rl); + return; + } + bytes_read = ntfs_rl_pread(vol, rl, 0, data_size, buf); + if (bytes_read != data_size) { + ntfs_log_perror("ntfs_rl_pread failed"); + free(buf); + free(rl); + return; + } + free(rl); + ea = (EA_ATTR*)buf; + } else { + ntfs_log_perror("ntfs_mapping_pairs_decompress failed"); + return; + } + } else { + data_size = le32_to_cpu(attr->value_length); + if (!opts.verbose) + return; + ea = (EA_ATTR*)((u8*)attr + le16_to_cpu(attr->value_offset)); + } + offset = 0; + while (1) { + printf("\n\tEA flags:\t\t "); + if (ea->flags) { + if (ea->flags == NEED_EA) + printf("NEED_EA\n"); + else + printf("Unknown (0x%02x)\n", + (unsigned)ea->flags); + } else + printf("NONE\n"); + printf("\tName length:\t %d (0x%x)\n", + (unsigned)ea->name_length, + (unsigned)ea->name_length); + printf("\tValue length:\t %d (0x%x)\n", + (unsigned)le16_to_cpu(ea->value_length), + (unsigned)le16_to_cpu(ea->value_length)); + /* Name expected to be null terminated ? */ + printf("\tName:\t\t '%s'\n", ea->name); + printf("\tValue:\t\t "); + if (ea->name_length == 11 && + !strncmp((const char*)"SETFILEBITS", + (const char*)ea->name, 11)) { + pval = (const le32*)(ea->value + ea->name_length + 1); + printf("0%lo\n", (unsigned long)le32_to_cpu(*pval)); + } else { + /* No alignment for value */ + pvalue = ea->value + ea->name_length + 1; + /* Hex show a maximum of 32 bytes */ + cnt = le16_to_cpu(ea->value_length); + printf(cnt ? "0x" : "(NONE)"); + if (cnt > 32) + cnt = 32; + while (cnt-- > 0) + printf("%02x",*pvalue++); + if (le16_to_cpu(ea->value_length) > 32) + printf("...\n"); + else + printf("\n"); + } + if (ea->next_entry_offset) { + offset += le32_to_cpu(ea->next_entry_offset); + ea = (const EA_ATTR*)((const u8*)ea + + le32_to_cpu(ea->next_entry_offset)); + } else + break; + if (offset >= data_size) + break; + } + free(buf); +} + +/** + * ntfs_dump_attr_property_set() + * + * dump the property_set attribute + */ +static void ntfs_dump_attr_property_set(ATTR_RECORD *attr __attribute__((unused))) +{ + /* TODO */ +} + +static void ntfs_hex_dump(void *buf,unsigned int length); + +/** + * ntfs_dump_attr_logged_utility_stream() + * + * dump the property_set attribute + */ +static void ntfs_dump_attr_logged_utility_stream(ATTR_RECORD *attr, + ntfs_inode *ni) +{ + char *buf; + s64 size; + + if (!opts.verbose) + return; + buf = ntfs_attr_readall(ni, AT_LOGGED_UTILITY_STREAM, + ntfs_attr_get_name(attr), attr->name_length, &size); + if (buf) + ntfs_hex_dump(buf, size); + free(buf); + /* TODO */ +} + +/** + * ntfs_hex_dump + */ +static void ntfs_hex_dump(void *buf,unsigned int length) +{ + unsigned int i=0; + while (i<length) { + unsigned int j; + + /* line start */ + printf("\t%04X ",i); + + /* hex content */ + for (j=i;(j<length) && (j<i+16);j++) { + unsigned char c = *((char *)buf + j); + printf("%02hhX ",c); + } + + /* realign */ + for (;j<i+16;j++) { + printf(" "); + } + + /* char content */ + for (j=i;(j<length) && (j<i+16);j++) { + unsigned char c = *((char *)buf + j); + /* display unprintable chars as '.' */ + if ((c<32) || (c>126)) { + c = '.'; + } + printf("%c",c); + } + + /* end line */ + printf("\n"); + i=j; + } +} + +/** + * ntfs_dump_attr_unknown + */ +static void ntfs_dump_attr_unknown(ATTR_RECORD *attr) +{ + printf("===== Please report this unknown attribute type to %s =====\n", + NTFS_DEV_LIST); + + if (!attr->non_resident) { + /* hex dump */ + printf("\tDumping some of the attribute data:\n"); + ntfs_hex_dump((u8*)attr + le16_to_cpu(attr->value_offset), + (le32_to_cpu(attr->value_length) > 128) ? + 128 : le32_to_cpu(attr->value_length)); + } +} + +/** + * ntfs_dump_inode_general_info + */ +static void ntfs_dump_inode_general_info(ntfs_inode *inode) +{ + MFT_RECORD *mrec = inode->mrec; + le16 inode_flags = mrec->flags; + + printf("Dumping Inode %llu (0x%llx)\n", + (long long)inode->mft_no, + (unsigned long long)inode->mft_no); + + ntfs_dump_usa_lsn("", mrec); + printf("MFT Record Seq. Numb.:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(mrec->sequence_number), + (unsigned)le16_to_cpu(mrec->sequence_number)); + printf("Number of Hard Links:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(mrec->link_count), + (unsigned)le16_to_cpu(mrec->link_count)); + printf("Attribute Offset:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(mrec->attrs_offset), + (unsigned)le16_to_cpu(mrec->attrs_offset)); + + printf("MFT Record Flags:\t "); + if (inode_flags) { + if (MFT_RECORD_IN_USE & inode_flags) { + printf("IN_USE "); + inode_flags &= ~MFT_RECORD_IN_USE; + } + if (MFT_RECORD_IS_DIRECTORY & inode_flags) { + printf("DIRECTORY "); + inode_flags &= ~MFT_RECORD_IS_DIRECTORY; + } + /* The meaning of IS_4 is illusive but not its existence. */ + if (MFT_RECORD_IS_4 & inode_flags) { + printf("IS_4 "); + inode_flags &= ~MFT_RECORD_IS_4; + } + if (MFT_RECORD_IS_VIEW_INDEX & inode_flags) { + printf("VIEW_INDEX "); + inode_flags &= ~MFT_RECORD_IS_VIEW_INDEX; + } + if (inode_flags) + printf("UNKNOWN: 0x%04x", (unsigned)le16_to_cpu( + inode_flags)); + } else { + printf("none"); + } + printf("\n"); + + printf("Bytes Used:\t\t %u (0x%x) bytes\n", + (unsigned)le32_to_cpu(mrec->bytes_in_use), + (unsigned)le32_to_cpu(mrec->bytes_in_use)); + printf("Bytes Allocated:\t %u (0x%x) bytes\n", + (unsigned)le32_to_cpu(mrec->bytes_allocated), + (unsigned)le32_to_cpu(mrec->bytes_allocated)); + + if (mrec->base_mft_record) { + printf("Base MFT Record:\t %llu (0x%llx)\n", + (unsigned long long) + MREF_LE(mrec->base_mft_record), + (unsigned long long) + MREF_LE(mrec->base_mft_record)); + } + printf("Next Attribute Instance: %u (0x%x)\n", + (unsigned)le16_to_cpu(mrec->next_attr_instance), + (unsigned)le16_to_cpu(mrec->next_attr_instance)); + + printf("MFT Padding:\t"); + ntfs_dump_bytes((u8 *)mrec, le16_to_cpu(mrec->usa_ofs) + + 2 * le16_to_cpu(mrec->usa_count), + le16_to_cpu(mrec->attrs_offset)); + printf("\n"); +} + +/** + * ntfs_get_file_attributes + */ +static void ntfs_dump_file_attributes(ntfs_inode *inode) +{ + struct RUNCOUNT runcount; + ntfs_attr_search_ctx *ctx = NULL; + + runcount.runs = 0; + runcount.fragments = 0; + /* then start enumerating attributes + see ntfs_attr_lookup documentation for detailed explanation */ + ctx = ntfs_attr_get_search_ctx(inode, NULL); + while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + if (ctx->attr->type == AT_END || ctx->attr->type == AT_UNUSED) { + printf("Weird: %s attribute type was found, please " + "report this.\n", + get_attribute_type_name( + ctx->attr->type)); + continue; + } + + ntfs_dump_attribute_header(ctx, inode->vol, &runcount); + + switch (ctx->attr->type) { + case AT_STANDARD_INFORMATION: + ntfs_dump_attr_standard_information(ctx->attr); + break; + case AT_ATTRIBUTE_LIST: + ntfs_dump_attr_list(ctx->attr, inode->vol); + break; + case AT_FILE_NAME: + ntfs_dump_attr_file_name(ctx->attr); + break; + case AT_OBJECT_ID: + ntfs_dump_attr_object_id(ctx->attr, inode->vol); + break; + case AT_SECURITY_DESCRIPTOR: + ntfs_dump_attr_security_descriptor(ctx->attr, + inode->vol); + break; + case AT_VOLUME_NAME: + ntfs_dump_attr_volume_name(ctx->attr); + break; + case AT_VOLUME_INFORMATION: + ntfs_dump_attr_volume_information(ctx->attr); + break; + case AT_DATA: + ntfs_dump_attr_data(ctx->attr, inode); + break; + case AT_INDEX_ROOT: + ntfs_dump_attr_index_root(ctx->attr, inode); + break; + case AT_INDEX_ALLOCATION: + ntfs_dump_attr_index_allocation(ctx->attr, inode); + break; + case AT_BITMAP: + ntfs_dump_attr_bitmap(ctx->attr); + break; + case AT_REPARSE_POINT: + ntfs_dump_attr_reparse_point(ctx->attr); + break; + case AT_EA_INFORMATION: + ntfs_dump_attr_ea_information(ctx->attr); + break; + case AT_EA: + ntfs_dump_attr_ea(ctx->attr, inode->vol); + break; + case AT_PROPERTY_SET: + ntfs_dump_attr_property_set(ctx->attr); + break; + case AT_LOGGED_UTILITY_STREAM: + ntfs_dump_attr_logged_utility_stream(ctx->attr, inode); + break; + default: + ntfs_dump_attr_unknown(ctx->attr); + } + } + + /* if we exited the loop before we're done - notify the user */ + if (errno != ENOENT) { + ntfs_log_perror("ntfsinfo error: stopped before finished " + "enumerating attributes"); + } else { + printf("End of inode reached\n"); + if (opts.verbose) { + printf("Total runs: %lu (fragments: %lu)\n", + runcount.runs, runcount.fragments); + } + } + + /* close all data-structures we used */ + ntfs_attr_put_search_ctx(ctx); + ntfs_inode_close(inode); +} + +/** + * main() - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char **argv) +{ + ntfs_volume *vol; + int res; + + setlinebuf(stdout); + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + res = parse_options(argc, argv); + if (res > 0) + printf("Failed to parse command line options\n"); + if (res >= 0) + exit(res); + + utils_set_locale(); + + vol = utils_mount_volume(opts.device, NTFS_MNT_RDONLY | + (opts.force ? NTFS_MNT_RECOVER : 0)); + if (!vol) { + printf("Failed to open '%s'.\n", opts.device); + exit(1); + } + + /* + * if opts.mft is not 0, then we will print out information about + * the volume, such as the sector size and whatnot. + */ + if (opts.mft) + ntfs_dump_volume(vol); + + if ((opts.inode != -1) || opts.filename) { + ntfs_inode *inode; + /* obtain the inode */ + if (opts.filename) { + inode = ntfs_pathname_to_inode(vol, NULL, + opts.filename); + } else { + inode = ntfs_inode_open(vol, MK_MREF(opts.inode, 0)); + } + + /* dump the inode information */ + if (inode) { + /* general info about the inode's mft record */ + ntfs_dump_inode_general_info(inode); + /* dump attributes */ + ntfs_dump_file_attributes(inode); + } else { + /* can't open inode */ + /* + * note: when the specified inode does not exist, either + * EIO or or ESPIPE is returned, we should notify better + * in those cases + */ + ntfs_log_perror("Error loading node"); + } + } + + ntfs_umount(vol, FALSE); + return 0; +} + diff --git a/ntfsprogs/ntfslabel.8 b/ntfsprogs/ntfslabel.8 new file mode 100755 index 0000000000000000000000000000000000000000..20f0690e70d7ee637263f0ff58a711e38f39782a --- /dev/null +++ b/ntfsprogs/ntfslabel.8 @@ -0,0 +1,119 @@ +.\" Copyright (c) 2002\-2004 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" Copyright (c) 2012 Jean-Pierre Andre. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSLABEL 8 "January 2012" "ntfs-3g 2015.3.14" +.SH NAME +ntfslabel \- display/change the label on an ntfs file system +.SH SYNOPSIS +.B ntfslabel +[\fIoptions\fR] \fIdevice \fR[\fInew\-label\fR] +.SH DESCRIPTION +.B ntfslabel +will display or change the file system label on the ntfs file system located on +.IR device . +It can also change the serial number of the +.IR device . +.PP +If the optional argument +.I new\-label +is not present, and no option is present, +.B ntfslabel +will simply display the current file system label. +.PP +If the optional argument +.I new\-label +is present, then +.B ntfslabel +will set the file system label to be +.IR new\-label . +NTFS file system labels can be at most 128 Unicode characters long; if +.I new\-label +is longer than 128 Unicode characters, +.B ntfslabel +will truncate it and print a warning message. +.PP +It is also possible to set the file system label using the +.B \-L +option of +.BR mkntfs (8) +during creation of the file system. +.SH OPTIONS +Below is a summary of all the options that +.B ntfslabel +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not working with a mounted +volume. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-\-new\-serial\fR[\fI=ssssssssssssssss\fR], or +.TP +\fB\-\-new\-half\-serial\fR[\fI=ssssssss\fR] +Set a new serial number to the device, either the argument value, or a random +one if no argument is given. The serial number is a 64 bit number, +represented as a sixteen-digit hexadecimal number, used to identify the +device during the mounting process. As a consequence, two devices with the +same serial number cannot be mounted at the same time on the same computer. +This is not the volume UUID used by Windows to locate files which have been +moved to another volume. + +The option \-\-new\-half\-serial only changes the upper part of the serial +number, keeping the lower part which is used by Windows unchanged. +In this case the optional argument is an eight-digit hexadecimal number. + +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Don't actually write to disk. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Reduce the amount of output to a minimum. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfslabel +prints. The label and the serial number are displayed. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license for +.BR ntfslabel . +.SH BUGS +There are no known problems with +.BR ntfslabel . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfslabel +was written by Matthew J. Fanto, with contributions from Anton Altaparmakov and +Richard Russon. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfslabel +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR mkntfs (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfslabel.8.in b/ntfsprogs/ntfslabel.8.in new file mode 100755 index 0000000000000000000000000000000000000000..dcd4d00ed5d797f9c98c27e86ed76f33538b9172 --- /dev/null +++ b/ntfsprogs/ntfslabel.8.in @@ -0,0 +1,119 @@ +.\" Copyright (c) 2002\-2004 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" Copyright (c) 2012 Jean-Pierre Andre. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSLABEL 8 "January 2012" "ntfs-3g @VERSION@" +.SH NAME +ntfslabel \- display/change the label on an ntfs file system +.SH SYNOPSIS +.B ntfslabel +[\fIoptions\fR] \fIdevice \fR[\fInew\-label\fR] +.SH DESCRIPTION +.B ntfslabel +will display or change the file system label on the ntfs file system located on +.IR device . +It can also change the serial number of the +.IR device . +.PP +If the optional argument +.I new\-label +is not present, and no option is present, +.B ntfslabel +will simply display the current file system label. +.PP +If the optional argument +.I new\-label +is present, then +.B ntfslabel +will set the file system label to be +.IR new\-label . +NTFS file system labels can be at most 128 Unicode characters long; if +.I new\-label +is longer than 128 Unicode characters, +.B ntfslabel +will truncate it and print a warning message. +.PP +It is also possible to set the file system label using the +.B \-L +option of +.BR mkntfs (8) +during creation of the file system. +.SH OPTIONS +Below is a summary of all the options that +.B ntfslabel +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not working with a mounted +volume. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-\-new\-serial\fR[\fI=ssssssssssssssss\fR], or +.TP +\fB\-\-new\-half\-serial\fR[\fI=ssssssss\fR] +Set a new serial number to the device, either the argument value, or a random +one if no argument is given. The serial number is a 64 bit number, +represented as a sixteen-digit hexadecimal number, used to identify the +device during the mounting process. As a consequence, two devices with the +same serial number cannot be mounted at the same time on the same computer. +This is not the volume UUID used by Windows to locate files which have been +moved to another volume. + +The option \-\-new\-half\-serial only changes the upper part of the serial +number, keeping the lower part which is used by Windows unchanged. +In this case the optional argument is an eight-digit hexadecimal number. + +.TP +\fB\-n\fR, \fB\-\-no\-action\fR +Don't actually write to disk. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Reduce the amount of output to a minimum. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfslabel +prints. The label and the serial number are displayed. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license for +.BR ntfslabel . +.SH BUGS +There are no known problems with +.BR ntfslabel . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfslabel +was written by Matthew J. Fanto, with contributions from Anton Altaparmakov and +Richard Russon. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfslabel +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR mkntfs (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfslabel.c b/ntfsprogs/ntfslabel.c new file mode 100755 index 0000000000000000000000000000000000000000..c0494ab5fd6246eeff75e7c736731db6ac62d309 --- /dev/null +++ b/ntfsprogs/ntfslabel.c @@ -0,0 +1,463 @@ +/** + * ntfslabel - Part of the Linux-NTFS project. + * + * Copyright (c) 2002 Matthew J. Fanto + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2003 Richard Russon + * Copyright (c) 2012-2014 Jean-Pierre Andre + * + * This utility will display/change the label on an NTFS partition. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "debug.h" +#include "mft.h" +#include "utils.h" +/* #include "version.h" */ +#include "logging.h" +#include "misc.h" + +static const char *EXEC_NAME = "ntfslabel"; + +static struct options { + char *device; /* Device/File to work with */ + char *label; /* Set the label to this */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int force; /* Override common sense */ + int new_serial; /* Change the serial number */ + unsigned long long serial; /* Forced serial number value */ + int noaction; /* Do not write to disk */ +} opts; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Display, or set, the label for an " + "NTFS Volume.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c)\n"); + ntfs_log_info(" 2002 Matthew J. Fanto\n"); + ntfs_log_info(" 2002-2005 Anton Altaparmakov\n"); + ntfs_log_info(" 2002-2003 Richard Russon\n"); + ntfs_log_info(" 2012-2014 Jean-Pierre Andre\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device [label]\n" + " -n, --no-action Do not write to disk\n" + " -f, --force Use less caution\n" + " --new-serial Set a new serial number\n" + " --new-half-serial Set a partial new serial number\n" + " -q, --quiet Less output\n" + " -v, --verbose More output\n" + " -V, --version Display version information\n" + " -h, --help Display this help\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char *argv[]) +{ + static const char *sopt = "-fh?IinqvV"; + static const struct option lopt[] = { + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "new-serial", optional_argument, NULL, 'I' }, + { "new-half-serial", optional_argument, NULL, 'i' }, + { "no-action", no_argument, NULL, 'n' }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 }, + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + char *endserial; + + opterr = 0; /* We'll handle the errors, thank you. */ + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!err && !opts.device) + opts.device = argv[optind-1]; + else if (!err && !opts.label) + opts.label = argv[optind-1]; + else + err++; + break; + case 'f': + opts.force++; + break; + case 'h': + help++; + break; + case 'I' : /* not proposed as a short option letter */ + if (optarg) { + opts.serial = strtoull(optarg, &endserial, 16); + if (*endserial) + ntfs_log_error("Bad hexadecimal serial number.\n"); + } + opts.new_serial |= 2; + break; + case 'i' : /* not proposed as a short option letter */ + if (optarg) { + opts.serial = strtoull(optarg, &endserial, 16) + << 32; + if (*endserial) + ntfs_log_error("Bad hexadecimal serial number.\n"); + } + opts.new_serial |= 1; + break; + case 'n': + opts.noaction++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + /* fall through */ + default: + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify a device.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at " + "the same time.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + /* tri-state 0 : done, 1 : error, -1 : proceed */ + return (err ? 1 : (help || ver ? 0 : -1)); +} + +static int change_serial(ntfs_volume *vol, u64 sector, le64 serial_number, + NTFS_BOOT_SECTOR *bs, NTFS_BOOT_SECTOR *oldbs) +{ + int res; + le64 mask; + BOOL same; + + res = -1; + if ((ntfs_pread(vol->dev, sector << vol->sector_size_bits, + vol->sector_size, bs) == vol->sector_size)) { + same = TRUE; + if (!sector) + /* save the real bootsector */ + memcpy(oldbs, bs, vol->sector_size); + else + /* backup bootsector must be similar */ + same = !memcmp(oldbs, bs, vol->sector_size); + if (same) { + if (opts.new_serial & 2) + bs->volume_serial_number = serial_number; + else { + mask = const_cpu_to_le64(~0x0ffffffffULL); + bs->volume_serial_number + = (serial_number & mask) + | (bs->volume_serial_number & ~mask); + } + if (opts.noaction + || (ntfs_pwrite(vol->dev, + sector << vol->sector_size_bits, + vol->sector_size, bs) == vol->sector_size)) { + res = 0; + } + } else { + ntfs_log_info("* Warning : the backup boot sector" + " does not match (leaving unchanged)\n"); + res = 0; + } + } + return (res); +} + +static int set_new_serial(ntfs_volume *vol) +{ + NTFS_BOOT_SECTOR *bs; /* full boot sectors */ + NTFS_BOOT_SECTOR *oldbs; /* full original boot sector */ + le64 serial_number; + u64 number_of_sectors; + u64 sn; + int res; + + res = -1; + bs = (NTFS_BOOT_SECTOR*)ntfs_malloc(vol->sector_size); + oldbs = (NTFS_BOOT_SECTOR*)ntfs_malloc(vol->sector_size); + if (bs && oldbs) { + if (opts.serial) + serial_number = cpu_to_le64(opts.serial); + else { + /* different values for parallel processes */ + srandom(time((time_t*)NULL) ^ (getpid() << 16)); + sn = ((u64)random() << 32) + | ((u64)random() & 0xffffffff); + serial_number = cpu_to_le64(sn); + } + if (!change_serial(vol, 0, serial_number, bs, oldbs)) { + number_of_sectors = le64_to_cpu(bs->number_of_sectors); + if (!change_serial(vol, number_of_sectors, + serial_number, bs, oldbs)) { + ntfs_log_info("New serial number : %016llx\n", + (long long)le64_to_cpu( + bs->volume_serial_number)); + res = 0; + } + } + free(bs); + free(oldbs); + } + if (res) + ntfs_log_info("Error setting a new serial number\n"); + return (res); +} + +static int print_serial(ntfs_volume *vol) +{ + NTFS_BOOT_SECTOR *bs; /* full boot sectors */ + int res; + + res = -1; + bs = (NTFS_BOOT_SECTOR*)ntfs_malloc(vol->sector_size); + if (bs + && (ntfs_pread(vol->dev, 0, + vol->sector_size, bs) == vol->sector_size)) { + ntfs_log_info("Serial number : %016llx\n", + (long long)le64_to_cpu(bs->volume_serial_number)); + res = 0; + free(bs); + } + if (res) + ntfs_log_info("Error getting the serial number\n"); + return (res); +} + +/** + * print_label - display the current label of a mounted ntfs partition. + * @dev: device to read the label from + * @mnt_flags: mount flags of the device or 0 if not mounted + * @mnt_point: mount point of the device or NULL + * + * Print the label of the device @dev. + */ +static int print_label(ntfs_volume *vol, unsigned long mnt_flags) +{ + int result = 0; + //XXX significant? + if ((mnt_flags & (NTFS_MF_MOUNTED | NTFS_MF_READONLY)) == + NTFS_MF_MOUNTED) { + ntfs_log_error("%s is mounted read-write, results may be " + "unreliable.\n", opts.device); + result = 1; + } + + if (opts.verbose) + ntfs_log_info("Volume label : %s\n", vol->vol_name); + else + ntfs_log_info("%s\n", vol->vol_name); + return result; +} + +/** + * change_label - change the current label on a device + * @dev: device to change the label on + * @mnt_flags: mount flags of the device or 0 if not mounted + * @mnt_point: mount point of the device or NULL + * @label: the new label + * + * Change the label on the device @dev to @label. + */ +static int change_label(ntfs_volume *vol, char *label) +{ + ntfschar *new_label = NULL; + int label_len; + int result = 0; + + label_len = ntfs_mbstoucs(label, &new_label); + if (label_len == -1) { + ntfs_log_perror("Unable to convert label string to Unicode"); + return 1; + } + else if (label_len*sizeof(ntfschar) > 0x100) { + ntfs_log_warning("New label is too long. Maximum %u characters " + "allowed. Truncating %u excess characters.\n", + (unsigned)(0x100 / sizeof(ntfschar)), + (unsigned)(label_len - + (0x100 / sizeof(ntfschar)))); + label_len = 0x100 / sizeof(ntfschar); + label[label_len] = const_cpu_to_le16(0); + } + + if(!opts.noaction) + result = ntfs_volume_rename(vol, new_label, label_len) ? 1 : 0; + + free(new_label); + return result; +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char **argv) +{ + unsigned long mnt_flags = 0; + int result = 0; + ntfs_volume *vol; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + result = parse_options(argc, argv); + if (result >= 0) + return (result); + + result = 0; + utils_set_locale(); + + if ((opts.label || opts.new_serial) + && !opts.noaction + && !opts.force + && !ntfs_check_if_mounted(opts.device, &mnt_flags) + && (mnt_flags & NTFS_MF_MOUNTED)) { + ntfs_log_error("Cannot make changes to a mounted device\n"); + result = 1; + goto abort; + } + + if (!opts.label && !opts.new_serial) + opts.noaction++; + + vol = utils_mount_volume(opts.device, + (opts.noaction ? NTFS_MNT_RDONLY : 0) | + (opts.force ? NTFS_MNT_RECOVER : 0)); + if (!vol) + return 1; + + if (opts.new_serial) { + result = set_new_serial(vol); + if (result) + goto unmount; + } else { + if (opts.verbose) + result = print_serial(vol); + } + if (opts.label) + result = change_label(vol, opts.label); + else + result = print_label(vol, mnt_flags); + +unmount : + ntfs_umount(vol, FALSE); +abort : + /* "result" may be a negative reply of a library function */ + return (result ? 1 : 0); +} + diff --git a/ntfsprogs/ntfsls.8 b/ntfsprogs/ntfsls.8 new file mode 100755 index 0000000000000000000000000000000000000000..382fa3ee11518a5e8bf45060a4dcc8def143c652 --- /dev/null +++ b/ntfsprogs/ntfsls.8 @@ -0,0 +1,172 @@ +.\" Copyright (c) 2003 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSLS 8 "November 2005" "ntfs-3g 2015.3.14" +.SH NAME +ntfsls \- list directory contents on an NTFS filesystem +.SH SYNOPSIS +.B ntfsls +[\fIoptions\fR] \fIdevice\fR +.sp +.B ntfsls +[ +.B \-a +| +.B \-\-all +] +[ +.B \-F +| +.B \-\-classify +] +[ +.B \-f +| +.B \-\-force +] +[ +.B \-h +| +.B \-\-help +] +[ +.B \-i +| +.B \-\-inode +] +[ +.B \-l +| +.B \-\-long +] +[ +.B \-p +| +.B \-\-path +.I PATH +] +[ +.B \-q +| +.B \-\-quiet +] +[ +.B \-s +| +.B \-\-system +] +[ +.B \-V +| +.B \-\-version +] +[ +.B \-v +| +.B \-\-verbose +] +[ +.B \-x +| +.B \-\-dos +] +.I device +.SH DESCRIPTION +.B ntfsls +is used to list information about the files specified by the +.I PATH +option (the root directory by default). +.I DEVICE +is the special file corresponding to the device (e.g +.IR /dev/hdXX ) +or an NTFS image file. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsls +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-a\fR, \fB\-\-all\fR +Display all files. If this option is not specified file names in the POSIX +namespace will not be displayed. +.TP +\fB\-F\fR, \fB\-\-classify\fR +Append indicator (one of */=@|) to entries. +.TP +\fB\-f\fR, \fB\-\-force\fR +Force execution. For example necessary to run on an NTFS partition stored in +a normal file. +.TP +\fB\-h\fR, \fB\-\-help\fR +Print the usage information of +.B ntfsls +and exit. +.TP +\fB\-i\fR, \fB\-\-inode\fR +Print inode number of each file. This is the MFT reference number in NTFS +terminology. +.TP +\fB\-l\fR, \fB\-\-long\fR +Use a long listing format. +.TP +\fB\-p\fR, \fB\-\-path\fR PATH +The directory whose contents to list or the file (including the path) about +which to display information. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-R\fR, \fB\-\-recursive\fR +Show the contents of all directories beneath the specified directory. +.TP +\fB\-s\fR, \fB\-\-system\fR +Unless this options is specified, all files beginning with a dollar sign +character will not be listed as these files are usually system files. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Print the version number of +.B ntfsls +and exit. +.TP +\fB\-x\fR, \fB\-\-dos\fR +Display short file names, i.e. files in the DOS namespace, instead of long +file names, i.e. files in the WIN32 namespace. +.SH BUGS +There are no known problems with +.BR ntfsls . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +This version of +.B ntfsls +was written by Lode Leroy, Anton Altaparmakov, Richard Russon, Carmelo Kintana +and Giang Nguyen. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfsls +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsls.8.in b/ntfsprogs/ntfsls.8.in new file mode 100755 index 0000000000000000000000000000000000000000..f528a3bb1a6447202c5aa580eb4cdaba8e7731ad --- /dev/null +++ b/ntfsprogs/ntfsls.8.in @@ -0,0 +1,172 @@ +.\" Copyright (c) 2003 Anton Altaparmakov. +.\" Copyright (c) 2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSLS 8 "November 2005" "ntfs-3g @VERSION@" +.SH NAME +ntfsls \- list directory contents on an NTFS filesystem +.SH SYNOPSIS +.B ntfsls +[\fIoptions\fR] \fIdevice\fR +.sp +.B ntfsls +[ +.B \-a +| +.B \-\-all +] +[ +.B \-F +| +.B \-\-classify +] +[ +.B \-f +| +.B \-\-force +] +[ +.B \-h +| +.B \-\-help +] +[ +.B \-i +| +.B \-\-inode +] +[ +.B \-l +| +.B \-\-long +] +[ +.B \-p +| +.B \-\-path +.I PATH +] +[ +.B \-q +| +.B \-\-quiet +] +[ +.B \-s +| +.B \-\-system +] +[ +.B \-V +| +.B \-\-version +] +[ +.B \-v +| +.B \-\-verbose +] +[ +.B \-x +| +.B \-\-dos +] +.I device +.SH DESCRIPTION +.B ntfsls +is used to list information about the files specified by the +.I PATH +option (the root directory by default). +.I DEVICE +is the special file corresponding to the device (e.g +.IR /dev/hdXX ) +or an NTFS image file. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsls +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-a\fR, \fB\-\-all\fR +Display all files. If this option is not specified file names in the POSIX +namespace will not be displayed. +.TP +\fB\-F\fR, \fB\-\-classify\fR +Append indicator (one of */=@|) to entries. +.TP +\fB\-f\fR, \fB\-\-force\fR +Force execution. For example necessary to run on an NTFS partition stored in +a normal file. +.TP +\fB\-h\fR, \fB\-\-help\fR +Print the usage information of +.B ntfsls +and exit. +.TP +\fB\-i\fR, \fB\-\-inode\fR +Print inode number of each file. This is the MFT reference number in NTFS +terminology. +.TP +\fB\-l\fR, \fB\-\-long\fR +Use a long listing format. +.TP +\fB\-p\fR, \fB\-\-path\fR PATH +The directory whose contents to list or the file (including the path) about +which to display information. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-R\fR, \fB\-\-recursive\fR +Show the contents of all directories beneath the specified directory. +.TP +\fB\-s\fR, \fB\-\-system\fR +Unless this options is specified, all files beginning with a dollar sign +character will not be listed as these files are usually system files. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Print the version number of +.B ntfsls +and exit. +.TP +\fB\-x\fR, \fB\-\-dos\fR +Display short file names, i.e. files in the DOS namespace, instead of long +file names, i.e. files in the WIN32 namespace. +.SH BUGS +There are no known problems with +.BR ntfsls . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +This version of +.B ntfsls +was written by Lode Leroy, Anton Altaparmakov, Richard Russon, Carmelo Kintana +and Giang Nguyen. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfsls +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsls.c b/ntfsprogs/ntfsls.c new file mode 100755 index 0000000000000000000000000000000000000000..909e7bb99f06ba0aad23b59b4c921f063ab8de39 --- /dev/null +++ b/ntfsprogs/ntfsls.c @@ -0,0 +1,717 @@ +/** + * ntfsls - Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Lode Leroy + * Copyright (c) 2003-2005 Anton Altaparmakov + * Copyright (c) 2003 Richard Russon + * Copyright (c) 2004 Carmelo Kintana + * Copyright (c) 2004 Giang Nguyen + * + * This utility will list a directory's files. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "types.h" +#include "mft.h" +#include "attrib.h" +#include "layout.h" +#include "inode.h" +#include "utils.h" +#include "dir.h" +#include "list.h" +#include "ntfstime.h" +/* #include "version.h" */ +#include "logging.h" + +static const char *EXEC_NAME = "ntfsls"; + +/** + * To hold sub-directory information for recursive listing. + * @depth: the level of this dir relative to opts.path + */ +struct dir { + struct ntfs_list_head list; + ntfs_inode *ni; + char name[MAX_PATH]; + int depth; +}; + +/** + * path_component - to store path component strings + * + * @name: string pointer + * + * NOTE: @name is not directly allocated memory. It simply points to the + * character array name in struct dir. + */ +struct path_component { + struct ntfs_list_head list; + const char *name; +}; + +/* The list of sub-dirs is like a "horizontal" tree. The root of + * the tree is opts.path, but it is not part of the list because + * that's not necessary. The rules of the list are (in order of + * precedence): + * 1. directories immediately follow their parent. + * 2. siblings are next to one another. + * + * For example, if: + * 1. opts.path is / + * 2. / has 2 sub-dirs: dir1 and dir2 + * 3. dir1 has 2 sub-dirs: dir11 and dir12 + * 4. dir2 has 0 sub-dirs + * then the list will be: + * dummy head -> dir1 -> dir11 -> dir12 -> dir2 + * + * dir_list_insert_pos keeps track of where to insert a sub-dir + * into the list. + */ +static struct ntfs_list_head *dir_list_insert_pos = NULL; + +/* The global depth relative to opts.path. + * ie: opts.path has depth 0, a sub-dir of opts.path has depth 1 + */ +static int depth = 0; + +static struct options { + char *device; /* Device/File to work with */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int force; /* Override common sense */ + int all; + int system; + int dos; + int lng; + int inode; + int classify; + int recursive; + const char *path; +} opts; + +typedef struct { + ntfs_volume *vol; +} ntfsls_dirent; + +static int list_dir_entry(ntfsls_dirent * dirent, const ntfschar * name, + const int name_len, const int name_type, + const s64 pos, const MFT_REF mref, + const unsigned dt_type); + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + printf("\n%s v%s (libntfs-3g) - Display information about an NTFS " + "Volume.\n\n", EXEC_NAME, VERSION); + printf("Copyright (c) 2003 Lode Leroy\n"); + printf("Copyright (c) 2003-2005 Anton Altaparmakov\n"); + printf("Copyright (c) 2003 Richard Russon\n"); + printf("Copyright (c) 2004 Carmelo Kintana\n"); + printf("Copyright (c) 2004 Giang Nguyen\n"); + printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + printf("\nUsage: %s [options] device\n" + "\n" + " -a, --all Display all files\n" + " -F, --classify Display classification\n" + " -f, --force Use less caution\n" + " -h, --help Display this help\n" + " -i, --inode Display inode numbers\n" + " -l, --long Display long info\n" + " -p, --path PATH Directory whose contents to list\n" + " -q, --quiet Less output\n" + " -R, --recursive Recursively list subdirectories\n" + " -s, --system Display system files\n" + " -V, --version Display version information\n" + " -v, --verbose More output\n" + " -x, --dos Use short (DOS 8.3) names\n" + "\n", + EXEC_NAME); + + printf("NOTE: If neither -a nor -s is specified, the program defaults to -a.\n\n"); + + printf("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char *argv[]) +{ + static const char *sopt = "-aFfh?ilp:qRsVvx"; + static const struct option lopt[] = { + { "all", no_argument, NULL, 'a' }, + { "classify", no_argument, NULL, 'F' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "inode", no_argument, NULL, 'i' }, + { "long", no_argument, NULL, 'l' }, + { "path", required_argument, NULL, 'p' }, + { "recursive", no_argument, NULL, 'R' }, + { "quiet", no_argument, NULL, 'q' }, + { "system", no_argument, NULL, 's' }, + { "version", no_argument, NULL, 'V' }, + { "verbose", no_argument, NULL, 'v' }, + { "dos", no_argument, NULL, 'x' }, + { NULL, 0, NULL, 0 }, + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + memset(&opts, 0, sizeof(opts)); + opts.device = NULL; + opts.path = "/"; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: + if (!opts.device) + opts.device = optarg; + else + err++; + break; + case 'p': + opts.path = optarg; + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + help++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + case 'x': + opts.dos = 1; + break; + case 'l': + opts.lng++; + break; + case 'i': + opts.inode++; + break; + case 'F': + opts.classify++; + break; + case 'a': + opts.all++; + break; + case 's': + opts.system++; + break; + case 'R': + opts.recursive++; + break; + default: + ntfs_log_error("Unknown option '%s'.\n", argv[optind - 1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + /* defaults to -a if -s is not specified */ + if (!opts.system) + opts.all++; + + if (help || ver) + opts.quiet = 0; + else { + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify exactly one " + "device.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the " + "same time.\n"); + err++; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + +/** + * free_dir - free one dir + * @tofree: the dir to free + * + * Close the inode and then free the dir + */ +static void free_dir(struct dir *tofree) +{ + if (tofree) { + if (tofree->ni) { + ntfs_inode_close(tofree->ni); + tofree->ni = NULL; + } + free(tofree); + } +} + +/** + * free_dirs - walk the list of dir's and free each of them + * @dir_list: the ntfs_list_head of any entry in the list + * + * Iterate over @dir_list, calling free_dir on each entry + */ +static void free_dirs(struct ntfs_list_head *dir_list) +{ + struct dir *tofree = NULL; + struct ntfs_list_head *walker = NULL; + + if (dir_list) { + ntfs_list_for_each(walker, dir_list) { + free_dir(tofree); + tofree = ntfs_list_entry(walker, struct dir, list); + } + + free_dir(tofree); + } +} + +/** + * readdir_recursive - list a directory and sub-directories encountered + * @ni: ntfs inode of the directory to list + * @pos: current position in directory + * @dirent: context for filldir callback supplied by the caller + * + * For each directory, print its path relative to opts.path. List a directory, + * then list each of its sub-directories. + * + * Returns 0 on success or -1 on error. + * + * NOTE: Assumes recursive option. Currently no limit on the depths of + * recursion. + */ +static int readdir_recursive(ntfs_inode * ni, s64 * pos, ntfsls_dirent * dirent) +{ + /* list of dirs to "ls" recursively */ + static struct dir dirs = { + .list = NTFS_LIST_HEAD_INIT(dirs.list), + .ni = NULL, + .name = {0}, + .depth = 0 + }; + + static struct path_component paths = { + .list = NTFS_LIST_HEAD_INIT(paths.list), + .name = NULL + }; + + static struct path_component base_comp; + + struct dir *subdir = NULL; + struct dir *tofree = NULL; + struct path_component comp; + struct path_component *tempcomp = NULL; + struct ntfs_list_head *dir_walker = NULL; + struct ntfs_list_head *comp_walker = NULL; + s64 pos2 = 0; + int ni_depth = depth; + int result = 0; + + if (ntfs_list_empty(&dirs.list)) { + base_comp.name = opts.path; + ntfs_list_add(&base_comp.list, &paths.list); + dir_list_insert_pos = &dirs.list; + printf("%s:\n", opts.path); + } + + depth++; + + result = ntfs_readdir(ni, pos, dirent, (ntfs_filldir_t) list_dir_entry); + + if (result == 0) { + ntfs_list_add_tail(&comp.list, &paths.list); + + /* for each of ni's sub-dirs: list in this iteration, then + free at the top of the next iteration or outside of loop */ + ntfs_list_for_each(dir_walker, &dirs.list) { + if (tofree) { + free_dir(tofree); + tofree = NULL; + } + subdir = ntfs_list_entry(dir_walker, struct dir, list); + + /* subdir is not a subdir of ni */ + if (subdir->depth != ni_depth + 1) + break; + + pos2 = 0; + dir_list_insert_pos = &dirs.list; + if (!subdir->ni) { + subdir->ni = + ntfs_pathname_to_inode(ni->vol, ni, + subdir->name); + + if (!subdir->ni) { + ntfs_log_error + ("ntfsls::readdir_recursive(): cannot get inode from pathname.\n"); + result = -1; + break; + } + } + puts(""); + + comp.name = subdir->name; + + /* print relative path header */ + ntfs_list_for_each(comp_walker, &paths.list) { + tempcomp = + ntfs_list_entry(comp_walker, + struct path_component, list); + printf("%s", tempcomp->name); + if (tempcomp != &comp + && *tempcomp->name != PATH_SEP + && (!opts.classify + || tempcomp == &base_comp)) + putchar(PATH_SEP); + } + puts(":"); + + result = readdir_recursive(subdir->ni, &pos2, dirent); + + if (result) + break; + + tofree = subdir; + ntfs_list_del(dir_walker); + } + + ntfs_list_del(&comp.list); + } + + if (tofree) + free_dir(tofree); + + /* if at the outer-most readdir_recursive, then clean up */ + if (ni_depth == 0) { + free_dirs(&dirs.list); + } + + depth--; + + return result; +} + +/** + * list_dir_entry + * + * FIXME: Should we print errors as we go along? (AIA) + */ +static int list_dir_entry(ntfsls_dirent * dirent, const ntfschar * name, + const int name_len, const int name_type, + const s64 pos __attribute__((unused)), + const MFT_REF mref, const unsigned dt_type) +{ + char *filename = NULL; + int result = 0; + + struct dir *dir = NULL; + + filename = calloc(1, MAX_PATH); + if (!filename) + return -1; + + if (ntfs_ucstombs(name, name_len, &filename, MAX_PATH) < 0) { + ntfs_log_error("Cannot represent filename in current locale.\n"); + goto free; + } + + result = 0; // These are successful + if ((MREF(mref) < FILE_first_user) && (!opts.system)) + goto free; + if (name_type == FILE_NAME_POSIX && !opts.all) + goto free; + if (((name_type & FILE_NAME_WIN32_AND_DOS) == FILE_NAME_WIN32) && + opts.dos) + goto free; + if (((name_type & FILE_NAME_WIN32_AND_DOS) == FILE_NAME_DOS) && + !opts.dos) + goto free; + if (dt_type == NTFS_DT_DIR && opts.classify) + sprintf(filename + strlen(filename), "/"); + + if (dt_type == NTFS_DT_DIR && opts.recursive + && strcmp(filename, ".") && strcmp(filename, "./") + && strcmp(filename, "..") && strcmp(filename, "../")) + { + dir = (struct dir *)calloc(1, sizeof(struct dir)); + + if (!dir) { + ntfs_log_error("Failed to allocate for subdir.\n"); + result = -1; + goto free; + } + + strcpy(dir->name, filename); + dir->ni = NULL; + dir->depth = depth; + } + + if (!opts.lng) { + if (!opts.inode) + printf("%s\n", filename); + else + printf("%7llu %s\n", (unsigned long long)MREF(mref), + filename); + result = 0; + } else { + s64 filesize = 0; + ntfs_inode *ni; + ntfs_attr_search_ctx *ctx = NULL; + FILE_NAME_ATTR *file_name_attr; + ATTR_RECORD *attr; + struct timespec change_time; + char t_buf[26]; + + result = -1; // Everything else is bad + + ni = ntfs_inode_open(dirent->vol, mref); + if (!ni) + goto release; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + goto release; + + if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, + 0, ctx)) + goto release; + attr = ctx->attr; + + file_name_attr = (FILE_NAME_ATTR *)((char *)attr + + le16_to_cpu(attr->value_offset)); + if (!file_name_attr) + goto release; + + change_time = ntfs2timespec(file_name_attr->last_data_change_time); + strcpy(t_buf, ctime(&change_time.tv_sec)); + memmove(t_buf+16, t_buf+19, 5); + t_buf[21] = '\0'; + + if (dt_type != NTFS_DT_DIR) { + if (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, + NULL, 0, ctx)) + filesize = ntfs_get_attribute_value_length( + ctx->attr); + } + + if (opts.inode) + printf("%7llu %8lld %s %s\n", + (unsigned long long)MREF(mref), + (long long)filesize, t_buf + 4, + filename); + else + printf("%8lld %s %s\n", (long long)filesize, t_buf + 4, + filename); + + if (dir) { + dir->ni = ni; + ni = NULL; /* so release does not close inode */ + } + + result = 0; +release: + /* Release attribute search context and close the inode. */ + if (ctx) + ntfs_attr_put_search_ctx(ctx); + if (ni) + ntfs_inode_close(ni); + } + + if (dir) { + if (result == 0) { + ntfs_list_add(&dir->list, dir_list_insert_pos); + dir_list_insert_pos = &dir->list; + } else { + free(dir); + dir = NULL; + } + } + +free: + free(filename); + return result; +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, parsing mount options failed + * 2 Error, mount attempt failed + * 3 Error, failed to open root directory + * 4 Error, failed to open directory in search path + */ +int main(int argc, char **argv) +{ + s64 pos; + ntfs_volume *vol; + ntfs_inode *ni; + ntfsls_dirent dirent; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + if (!parse_options(argc, argv)) { + // FIXME: Print error... (AIA) + return 1; + } + + utils_set_locale(); + + vol = utils_mount_volume(opts.device, NTFS_MNT_RDONLY | + (opts.force ? NTFS_MNT_RECOVER : 0)); + if (!vol) { + // FIXME: Print error... (AIA) + return 2; + } + + ni = ntfs_pathname_to_inode(vol, NULL, opts.path); + if (!ni) { + // FIXME: Print error... (AIA) + ntfs_umount(vol, FALSE); + return 3; + } + + /* + * We now are at the final path component. If it is a file just + * list it. If it is a directory, list its contents. + */ + pos = 0; + memset(&dirent, 0, sizeof(dirent)); + dirent.vol = vol; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (opts.recursive) + readdir_recursive(ni, &pos, &dirent); + else + ntfs_readdir(ni, &pos, &dirent, + (ntfs_filldir_t) list_dir_entry); + // FIXME: error checking... (AIA) + } else { + ATTR_RECORD *rec; + FILE_NAME_ATTR *attr; + ntfs_attr_search_ctx *ctx; + int space = 4; + ntfschar *name = NULL; + int name_len = 0;; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + + while ((rec = find_attribute(AT_FILE_NAME, ctx))) { + /* We know this will always be resident. */ + attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset)); + + if (attr->file_name_type < space) { + name = attr->file_name; + name_len = attr->file_name_length; + space = attr->file_name_type; + } + } + + list_dir_entry(&dirent, name, name_len, space, pos, ni->mft_no, + NTFS_DT_REG); + // FIXME: error checking... (AIA) + + ntfs_attr_put_search_ctx(ctx); + } + + /* Finished with the inode; release it. */ + ntfs_inode_close(ni); + + ntfs_umount(vol, FALSE); + return 0; +} + diff --git a/ntfsprogs/ntfsmftalloc.c b/ntfsprogs/ntfsmftalloc.c new file mode 100755 index 0000000000000000000000000000000000000000..246ab54a88d70894bfb4da556c6fa5947e596617 --- /dev/null +++ b/ntfsprogs/ntfsmftalloc.c @@ -0,0 +1,368 @@ +/** + * ntfsmftalloc - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * + * This utility will allocate and initialize an mft record. + * + * 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 (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +# include <stdio.h> +#endif +#ifdef HAVE_STDARG_H +# include <stdarg.h> +#endif +#ifdef HAVE_STRING_H +# include <string.h> +#endif +#ifdef HAVE_ERRNO_H +# include <errno.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_GETOPT_H +# include <getopt.h> +#else + extern int optind; +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifndef LLONG_MAX +# define LLONG_MAX 9223372036854775807LL +#endif + +#include "types.h" +#include "attrib.h" +#include "inode.h" +#include "layout.h" +#include "volume.h" +#include "mft.h" +#include "utils.h" +/* #include "version.h" */ +#include "logging.h" + +static const char *EXEC_NAME = "ntfsmftalloc"; + +/* Need these global so ntfsmftalloc_exit can access them. */ +static BOOL success = FALSE; + +static char *dev_name; + +static ntfs_volume *vol; +static ntfs_inode *ni = NULL; +static ntfs_inode *base_ni = NULL; +static s64 base_mft_no = -1; + +static struct { + /* -h, print usage and exit. */ + int no_action; /* -n, do not write to device, only display + what would be done. */ + int quiet; /* -q, quiet execution. */ + int verbose; /* -v, verbose execution, given twice, really + verbose execution (debug mode). */ + int force; /* -f, force allocation. */ + /* -V, print version and exit. */ +} opts; + +/** + * err_exit - error output and terminate; ignores quiet (-q) + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static void err_exit(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "Aborting...\n"); + exit(1); +} + +/** + * copyright - print copyright statements + */ +static void copyright(void) +{ + ntfs_log_info("Copyright (c) 2004-2005 Anton Altaparmakov\n" + "Allocate and initialize a base or an extent mft " + "record. If a base mft record\nis not specified, a " + "base mft record is allocated and initialized. " + "Otherwise,\nan extent mft record is allocated and " + "initialized to point to the specified\nbase mft " + "record.\n"); +} + +/** + * license - print license statement + */ +static void license(void) +{ + ntfs_log_info("%s", ntfs_gpl); +} + +/** + * usage - print a list of the parameters to the program + */ +__attribute__((noreturn)) +static void usage(void) +{ + copyright(); + ntfs_log_info("Usage: %s [options] device [base-mft-record]\n" + " -n Do not write to disk\n" + " -f Force execution despite errors\n" + " -q Quiet execution\n" + " -v Verbose execution\n" + " -vv Very verbose execution\n" + " -V Display version information\n" + " -l Display licensing information\n" + " -h Display this help\n", EXEC_NAME); + ntfs_log_info("%s%s", ntfs_bugs, ntfs_home); + exit(1); +} + +/** + * parse_options + */ +static void parse_options(int argc, char *argv[]) +{ + long long ll; + char *s; + int c; + + if (argc && *argv) + EXEC_NAME = *argv; + ntfs_log_info("%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + while ((c = getopt(argc, argv, "fh?nqvVl")) != EOF) { + switch (c) { + case 'f': + opts.force = 1; + break; + case 'n': + opts.no_action = 1; + break; + case 'q': + opts.quiet = 1; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + /* Version number already printed, so just exit. */ + exit(0); + case 'l': + copyright(); + license(); + exit(0); + case 'h': + case '?': + default: + usage(); + } + } + + if (opts.verbose > 1) + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | + NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET); + + if (optind == argc) + usage(); + /* Get the device. */ + dev_name = argv[optind++]; + ntfs_log_verbose("device name = %s\n", dev_name); + if (optind != argc) { + /* Get the base mft record number. */ + ll = strtoll(argv[optind++], &s, 0); + if (*s || !ll || (ll >= LLONG_MAX && errno == ERANGE)) + err_exit("Invalid base mft record number: %s\n", + argv[optind - 1]); + base_mft_no = ll; + ntfs_log_verbose("base mft record number = 0x%llx\n", (long long)ll); + } + if (optind != argc) + usage(); +} + +/** + * dump_mft_record + */ +static void dump_mft_record(MFT_RECORD *m) +{ + ATTR_RECORD *a; + unsigned int u; + MFT_REF r; + + ntfs_log_info("-- Beginning dump of mft record. --\n"); + u = le32_to_cpu(m->magic); + ntfs_log_info("Mft record signature (magic) = %c%c%c%c\n", u & 0xff, + u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff); + u = le16_to_cpu(m->usa_ofs); + ntfs_log_info("Update sequence array offset = %u (0x%x)\n", u, u); + ntfs_log_info("Update sequence array size = %u\n", le16_to_cpu(m->usa_count)); + ntfs_log_info("$LogFile sequence number (lsn) = %llu\n", + (unsigned long long)le64_to_cpu(m->lsn)); + ntfs_log_info("Sequence number = %u\n", le16_to_cpu(m->sequence_number)); + ntfs_log_info("Reference (hard link) count = %u\n", + le16_to_cpu(m->link_count)); + u = le16_to_cpu(m->attrs_offset); + ntfs_log_info("First attribute offset = %u (0x%x)\n", u, u); + ntfs_log_info("Flags = %u: ", le16_to_cpu(m->flags)); + if (m->flags & MFT_RECORD_IN_USE) + ntfs_log_info("MFT_RECORD_IN_USE"); + else + ntfs_log_info("MFT_RECORD_NOT_IN_USE"); + if (m->flags & MFT_RECORD_IS_DIRECTORY) + ntfs_log_info(" | MFT_RECORD_IS_DIRECTORY"); + ntfs_log_info("\n"); + u = le32_to_cpu(m->bytes_in_use); + ntfs_log_info("Bytes in use = %u (0x%x)\n", u, u); + u = le32_to_cpu(m->bytes_allocated); + ntfs_log_info("Bytes allocated = %u (0x%x)\n", u, u); + r = le64_to_cpu(m->base_mft_record); + ntfs_log_info("Base mft record reference:\n\tMft record number = %llu\n\t" + "Sequence number = %u\n", + (unsigned long long)MREF(r), MSEQNO(r)); + ntfs_log_info("Next attribute instance = %u\n", + le16_to_cpu(m->next_attr_instance)); + a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); + ntfs_log_info("-- Beginning dump of attributes within mft record. --\n"); + while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) { + if (a->type == AT_END) + break; + a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); + }; + ntfs_log_info("-- End of attributes. --\n"); +} + +/** + * ntfsmftalloc_exit + */ +static void ntfsmftalloc_exit(void) +{ + if (success) + return; + /* If there is a base inode, close that instead of the extent inode. */ + if (base_ni) + ni = base_ni; + /* Close the inode. */ + if (ni && ntfs_inode_close(ni)) { + ntfs_log_perror("Warning: Failed to close inode 0x%llx", + (long long)ni->mft_no); + } + /* Unmount the volume. */ + if (ntfs_umount(vol, 0) == -1) + ntfs_log_perror("Warning: Could not umount %s", dev_name); +} + +/** + * main + */ +int main(int argc, char **argv) +{ + unsigned long mnt_flags, ul; + int err; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + /* Initialize opts to zero / required values. */ + memset(&opts, 0, sizeof(opts)); + /* Parse command line options. */ + parse_options(argc, argv); + utils_set_locale(); + /* Make sure the file system is not mounted. */ + if (ntfs_check_if_mounted(dev_name, &mnt_flags)) + ntfs_log_error("Failed to determine whether %s is mounted: %s\n", + dev_name, strerror(errno)); + else if (mnt_flags & NTFS_MF_MOUNTED) { + ntfs_log_error("%s is mounted.\n", dev_name); + if (!opts.force) + err_exit("Refusing to run!\n"); + ntfs_log_error("ntfsmftalloc forced anyway. Hope /etc/mtab " + "is incorrect.\n"); + } + /* Mount the device. */ + if (opts.no_action) { + ntfs_log_quiet("Running in READ-ONLY mode!\n"); + ul = NTFS_MNT_RDONLY; + } else + ul = 0; + vol = ntfs_mount(dev_name, ul); + if (!vol) + err_exit("Failed to mount %s: %s\n", dev_name, strerror(errno)); + /* Register our exit function which will unlock and close the device. */ + err = atexit(&ntfsmftalloc_exit); + if (err == -1) { + ntfs_log_error("Could not set up exit() function because atexit() " + "failed: %s Aborting...\n", strerror(errno)); + ntfsmftalloc_exit(); + exit(1); + } + if (base_mft_no != -1) { + base_ni = ntfs_inode_open(vol, base_mft_no); + if (!base_ni) + err_exit("Failed to open base inode 0x%llx: %s\n", + (long long)base_mft_no, + strerror(errno)); + } + /* Open the specified inode. */ + ni = ntfs_mft_record_alloc(vol, base_ni); + if (!ni) + err_exit("Failed to allocate mft record: %s\n", + strerror(errno)); + ntfs_log_info("Allocated %s mft record 0x%llx", base_ni ? "extent" : "base", + (long long)ni->mft_no); + if (base_ni) + ntfs_log_info(" with base mft record 0x%llx", + (long long)base_mft_no); + ntfs_log_info(".\n"); + if (!opts.quiet && opts.verbose > 1) { + ntfs_log_verbose("Dumping allocated mft record 0x%llx:\n", + (long long)ni->mft_no); + dump_mft_record(ni->mrec); + } + /* Close the (base) inode. */ + if (base_ni) + ni = base_ni; + err = ntfs_inode_close(ni); + if (err) + err_exit("Failed to close inode 0x%llx: %s\n", + (long long)ni->mft_no, strerror(errno)); + /* Unmount the volume. */ + err = ntfs_umount(vol, 0); + /* Disable our ntfsmftalloc_exit() handler. */ + success = TRUE; + if (err == -1) + ntfs_log_perror("Warning: Failed to umount %s", dev_name); + else + ntfs_log_quiet("ntfsmftalloc completed successfully.\n"); + return 0; +} diff --git a/ntfsprogs/ntfsmove.c b/ntfsprogs/ntfsmove.c new file mode 100755 index 0000000000000000000000000000000000000000..571808fdb0f82873fb9612bc6ae74c7f34090733 --- /dev/null +++ b/ntfsprogs/ntfsmove.c @@ -0,0 +1,923 @@ +/** + * ntfsmove - Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Richard Russon + * Copyright (c) 2003-2005 Anton Altaparmakov + * + * This utility will move files on an NTFS volume. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "types.h" +#include "attrib.h" +#include "utils.h" +#include "volume.h" +#include "debug.h" +#include "dir.h" +#include "bitmap.h" +#include "ntfsmove.h" +/* #include "version.h" */ +#include "logging.h" + +static const char *EXEC_NAME = "ntfsmove"; +static struct options opts; + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Move files and directories on an " + "NTFS volume.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2003 Richard Russon\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device file\n" + "\n" + " -S --start Move to the start of the volume\n" + " -B --best Move to the best place on the volume\n" + " -E --end Move to the end of the volume\n" + " -C num --cluster num Move to this cluster offset\n" + "\n" + " -D --no-dirty Do not mark volume dirty (require chkdsk)\n" + " -n --no-action Do not write to disk\n" + " -f --force Use less caution\n" + " -h --help Print this help\n" + " -q --quiet Less output\n" + " -V --version Version information\n" + " -v --verbose More output\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-BC:DEfh?nqSVv"; + static const struct option lopt[] = { + { "best", no_argument, NULL, 'B' }, + { "cluster", required_argument, NULL, 'C' }, + { "end", no_argument, NULL, 'E' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "no-action", no_argument, NULL, 'n' }, + { "no-dirty", no_argument, NULL, 'D' }, + { "quiet", no_argument, NULL, 'q' }, + { "start", no_argument, NULL, 'S' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + char *end = NULL; + + opterr = 0; /* We'll handle the errors, thank you. */ + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind-1]; + } else if (!opts.file) { + opts.file = argv[optind-1]; + } else { + opts.device = NULL; + opts.file = NULL; + err++; + } + break; + case 'B': + if (opts.location == 0) + opts.location = NTFS_MOVE_LOC_BEST; + else + opts.location = -1; + break; + case 'C': + if (opts.location == 0) { + opts.location = strtoll(optarg, &end, 0); + if (end && *end) + err++; + } else { + opts.location = -1; + } + break; + case 'D': + opts.nodirty++; + break; + case 'E': + if (opts.location == 0) + opts.location = NTFS_MOVE_LOC_END; + else + opts.location = -1; + break; + case 'f': + opts.force++; + break; + case 'h': + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + help++; + break; + case 'n': + opts.noaction++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'S': + if (opts.location == 0) + opts.location = NTFS_MOVE_LOC_START; + else + opts.location = -1; + break; + case 'V': + ver++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + default: + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if ((opts.device == NULL) || + (opts.file == NULL)) { + if (argc > 1) + ntfs_log_error("You must specify one device and one file.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the " + "same time.\n"); + err++; + } + + if (opts.location == -1) { + ntfs_log_error("You may only specify one location option: " + "--start, --best, --end or --cluster\n"); + err++; + } else if (opts.location == 0) { + opts.location = NTFS_MOVE_LOC_BEST; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + return (!err && !help && !ver); +} + +#if 0 + +/** + * ntfs_debug_runlist_dump2 - Dump a runlist. + */ +static int ntfs_debug_runlist_dump2(const runlist *rl, int abbr, char *prefix) +{ + //int abbr = 3; /* abbreviate long lists */ + int len = 0; + int i; + int res = 0; + u64 total = 0; + const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "EINVAL", "XXXX" }; + + if (!rl) { + ntfs_log_info(" Run list not present.\n"); + return 0; + } + + if (!prefix) + prefix = ""; + + if (abbr) + for (len = 0; rl[len].length; len++) ; + + ntfs_log_info("%s VCN LCN len\n", prefix); + for (i = 0; rl->length; i++, rl++) { + LCN lcn = rl->lcn; + + total += rl->length; + if (abbr) + if (len > 20) { + if ((i == abbr) && (len > (abbr*2))) + ntfs_log_info("%s ... ... ...\n", prefix); + if ((i > (abbr-1)) && (i < (len - (abbr-1)))) + continue; + } + + if (rl->vcn < -1) + res = -1; + + if (lcn < (LCN)0) { + int j = -lcn - 1; + + if ((j < 0) || (j > 4)) { + j = 4; + res = -1; + } + ntfs_log_info("%s%8lld %8s %8lld\n", prefix, + rl->vcn, lcn_str[j], rl->length); + } else + ntfs_log_info("%s%8lld %8lld %8lld\n", prefix, + rl->vcn, rl->lcn, rl->length); + } + ntfs_log_info("%s --------\n", prefix); + ntfs_log_info("%s %8lld\n", prefix, total); + ntfs_log_info("\n"); + return res; +} + +#endif /* if 0 */ + +/** + * resize_nonres_attr + */ +static int resize_nonres_attr(MFT_RECORD *m, ATTR_RECORD *a, const u32 new_size) +{ + int this_attr; + int next_attr; + int tail_size; + int file_size; + int old_size; + u8 *ptr; + + old_size = a->length; + file_size = m->bytes_in_use; + this_attr = p2n(a)-p2n(m); + next_attr = this_attr + a->length; + tail_size = file_size - next_attr; + ptr = (u8*) m; + + /* + ntfs_log_info("old_size = %d\n", old_size); + ntfs_log_info("new_size = %d\n", new_size); + ntfs_log_info("file_size = %d\n", file_size); + ntfs_log_info("this_attr = %d\n", this_attr); + ntfs_log_info("next_attr = %d\n", next_attr); + ntfs_log_info("tail_size = %d\n", tail_size); + */ + + memmove(ptr + this_attr + new_size, ptr + next_attr, tail_size); + + a->length = new_size; + m->bytes_in_use += new_size - old_size; + + return 0; +} + +/** + * calc_attr_length + */ +static int calc_attr_length(ATTR_RECORD *rec, int runlength) +{ + int size; + + if (!rec) + return -1; + if (!rec->non_resident) + return -1; + + size = rec->mapping_pairs_offset + runlength + 7; + size &= 0xFFF8; + return size; +} + +#if 0 + +/** + * dump_runs + */ +static void dump_runs(u8 *buffer, int len) +{ + int i; + ntfs_log_info("RUN: \e[01;31m"); + + for (i = 0; i < len; i++) { + ntfs_log_info(" %02x", buffer[i]); + } + ntfs_log_info("\e[0m\n"); +} + +#endif /* if 0 */ + +/** + * find_unused + */ +static runlist * find_unused(ntfs_volume *vol, s64 size, u64 loc + __attribute__((unused)), int flags __attribute__((unused))) +{ + const int bufsize = 8192; + u8 *buffer; + int clus; + int i; + int curr = 0; + int count = 0; + s64 start = 0; + int bit = 0; + runlist *res = NULL; + + //ntfs_log_info("find_unused\n"); + buffer = malloc(bufsize); + if (!buffer) { + ntfs_log_info("!buffer\n"); + return NULL; + } + + //ntfs_log_info("looking for space for %lld clusters\n", size); + + clus = vol->lcnbmp_na->allocated_size / bufsize; + //ntfs_log_info("clus = %d\n", clus); + + for (i = 0; i < clus; i++) { + int bytes_read, j; + + bytes_read = ntfs_attr_pread(vol->lcnbmp_na, i*bufsize, + bufsize, buffer); + if (bytes_read != bufsize) { + ntfs_log_info("!read\n"); + return NULL; + } + for (j = 0; j < bufsize*8; j++) { + bit = !!test_bit(j & 7, buffer[j>>3]); + if (curr == bit) { + count++; + if ((!bit) && (count >= size)) { + //res = calloc(2, sizeof(*res)); + res = calloc(1, 4096); + if (res) { + res[0].vcn = 0; + res[0].lcn = start; + res[0].length = size; + res[1].lcn = LCN_ENOENT; + } + goto done; + } + } else { + //ntfs_log_info("%d * %d\n", curr, count); + curr = bit; + count = 1; + start = i*bufsize*8 + j; + } + } + } +done: + //ntfs_log_info("%d * %d\n", curr, count); + + free(buffer); + + if (res) { + for (i = 0; i < size; i++) { + if (utils_cluster_in_use(vol, res->lcn + i)) { + ntfs_log_info("ERROR cluster %lld in use\n", + (long long)res->lcn + i); + } + } + } else { + ntfs_log_info("failed\n"); + } + + return res; +} + +/** + * dont_move + * + * Don't let the user move: + * ANY metadata + * Any fragmented MFT records + * The boot file 'ntldr' + */ +static int dont_move(ntfs_inode *ino) +{ + static const ntfschar ntldr[6] = { + const_cpu_to_le16('n'), const_cpu_to_le16('t'), const_cpu_to_le16('l'), + const_cpu_to_le16('d'), const_cpu_to_le16('r'), const_cpu_to_le16('\0') + }; + + ATTR_RECORD *rec; + FILE_NAME_ATTR *name; + + if (utils_is_metadata(ino)) { + ntfs_log_error("metadata\n"); + return 1; + } + + rec = find_first_attribute(AT_ATTRIBUTE_LIST, ino->mrec); + if (rec) { + ntfs_log_error("attribute list\n"); + return 1; + } + + rec = find_first_attribute(AT_FILE_NAME, ino->mrec); + if (!rec) { + ntfs_log_error("extend inode\n"); + return 1; + } + + name = (FILE_NAME_ATTR*) ((u8*)rec + rec->value_offset); + if (ntfs_names_are_equal(ntldr, 5, name->file_name, name->file_name_length, + IGNORE_CASE, ino->vol->upcase, ino->vol->upcase_len)) { + ntfs_log_error("ntldr\n"); + return 1; + } + + return 0; +} + + +/** + * bitmap_alloc + */ +static int bitmap_alloc(ntfs_volume *vol, runlist_element *rl) +{ + int res; + + if (!rl) + return -1; + + res = ntfs_bitmap_set_run(vol->lcnbmp_na, rl->lcn, rl->length); + if (res < 0) { + ntfs_log_error("bitmap alloc returns %d\n", res); + } + + return res; +} + +/** + * bitmap_free + */ +static int bitmap_free(ntfs_volume *vol, runlist_element *rl) +{ + int res; + + if (!rl) + return -1; + + res = ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, rl->length); + if (res < 0) { + ntfs_log_error("bitmap free returns %d\n", res); + } + + return res; +} + +/** + * data_copy + */ +static int data_copy(ntfs_volume *vol, runlist_element *from, runlist_element *to) +{ + int i; + u8 *buffer; + s64 res = 0; + + if (!vol || !from || !to) + return -1; + if ((from->length != to->length) || (from->lcn < 0) || (to->lcn < 0)) + return -1; + + //ntfs_log_info("data_copy: from 0x%llx to 0x%llx\n", from->lcn, to->lcn); + buffer = malloc(vol->cluster_size); + if (!buffer) { + ntfs_log_info("!buffer\n"); + return -1; + } + + for (i = 0; i < from->length; i++) { + //ntfs_log_info("read cluster at %8lld\n", from->lcn+i); + res = ntfs_pread(vol->dev, (from->lcn+i) * vol->cluster_size, + vol->cluster_size, buffer); + if (res != vol->cluster_size) { + ntfs_log_error("!read\n"); + res = -1; + break; + } + + //ntfs_log_info("write cluster to %8lld\n", to->lcn+i); + res = ntfs_pwrite(vol->dev, (to->lcn+i) * vol->cluster_size, + vol->cluster_size, buffer); + if (res != vol->cluster_size) { + ntfs_log_error("!write %lld\n", (long long)res); + res = -1; + break; + } + } + + free(buffer); + return res; +} + +/** + * move_runlist + * + * validate: + * runlists are the same size + * from in use + * to not in use + * allocate new space + * copy data + * deallocate old space + */ +static s64 move_runlist(ntfs_volume *vol, runlist_element *from, + runlist_element *to) +{ + int i; + + if (!vol || !from || !to) + return -1; + if (from->length != to->length) { + ntfs_log_error("diffsizes\n"); + return -1; + } + + if ((from->lcn < 0) || (to->lcn < 0)) { + ntfs_log_error("invalid runs\n"); + return -1; + } + + for (i = 0; i < from->length; i++) { + if (!utils_cluster_in_use(vol, from->lcn+i)) { + ntfs_log_error("from not in use\n"); + return -1; + } + } + + for (i = 0; i < to->length; i++) { + if (utils_cluster_in_use(vol, to->lcn+i)) { + ntfs_log_error("to is in use\n"); + return -1; + } + } + + if (bitmap_alloc(vol, to) < 0) { + ntfs_log_error("cannot bitmap_alloc\n"); + return -1; + } + + if (data_copy(vol, from, to) < 0) { + ntfs_log_error("cannot data_copy\n"); + return -1; + } + + if (bitmap_free(vol, from) < 0) { + ntfs_log_error("cannot bitmap_free\n"); + return -1; + } + + return 0; +} + + +/**original + * move_datarun + * > 0 Bytes moved / size to be moved + * = 0 Nothing to do + * < 0 Error + */ + +// get size of runlist +// find somewhere to put data +// backup original runlist +// move the data + +// got to get the runlist out of this function +// requires a mrec arg, not an ino (ino->mrec will do for now) +// check size of new runlist before allocating / moving +// replace one datarun with another (by hand) +static s64 move_datarun(ntfs_volume *vol, ntfs_inode *ino, ATTR_RECORD *rec, + runlist_element *run, u64 loc, int flags) +{ + runlist *from; + runlist *to; + int need_from; + int need_to; + int i; + s64 res = -1; + + // find empty space + to = find_unused(vol, run->length, loc, flags); + if (!to) { + ntfs_log_error("!to\n"); + return -1; + } + + to->vcn = run->vcn; + + // copy original runlist + from = ntfs_mapping_pairs_decompress(vol, rec, NULL); + if (!from) { + ntfs_log_info("!from\n"); + return -1; + } + + ntfs_log_info("move %lld,%lld,%lld to %lld,%lld,%lld\n", + (long long)run->vcn, (long long)run->lcn, (long long)run->length, + (long long)to->vcn, (long long)to->lcn, (long long)to->length); + + need_from = ntfs_get_size_for_mapping_pairs(vol, from, 0, INT_MAX); + ntfs_log_info("orig data run = %d bytes\n", need_from); + + //ntfs_debug_runlist_dump2(from, 5, "\t"); + + for (i = 0; to[i].length > 0; i++) { + if (from[i].vcn == run->vcn) { + from[i].lcn = to->lcn; + break; + } + } + + //ntfs_debug_runlist_dump2(from, 5, "\t"); + + need_to = ntfs_get_size_for_mapping_pairs(vol, from, 0, INT_MAX); + ntfs_log_info("new data run = %d bytes\n", need_to); + + need_from = calc_attr_length(rec, need_from); + need_to = calc_attr_length(rec, need_to); + + ntfs_log_info("Before %d, after %d\n", need_from, need_to); + + if (need_from != need_to) { + if (resize_nonres_attr(ino->mrec, rec, need_to) < 0) { + ntfs_log_info("!resize\n"); + return -1; + } + } + + res = move_runlist(vol, run, to); + if (res < 0) { + ntfs_log_error("!move_runlist\n"); + return -1; + } + + // wipe orig runs + memset(((u8*)rec) +rec->mapping_pairs_offset, 0, need_to - rec->mapping_pairs_offset); + + // update data runs + ntfs_mapping_pairs_build(vol, ((u8*)rec) + rec->mapping_pairs_offset, + need_to, from, 0, NULL); + + // commit + ntfs_inode_mark_dirty(ino); + + if (ntfs_inode_sync(ino) < 0) { + ntfs_log_info("!sync\n"); + return -1; + } + + free(from); + free(to); + return res; +} + +/** + * move_attribute - + * + * > 0 Bytes moved / size to be moved + * = 0 Nothing to do + * < 0 Error + */ +static s64 move_attribute(ntfs_volume *vol, ntfs_inode *ino, ATTR_RECORD *rec, + u64 loc, int flags) +{ + int i; + s64 res; + s64 count = 0; + runlist *runs; + + // NTFS_MOVE_LOC_BEST : assess how much space this attribute will need, + // find that space and pass the location to our children. + // Anything else we pass directly to move_datarun. + + runs = ntfs_mapping_pairs_decompress(vol, rec, NULL); + if (!runs) { + ntfs_log_error("!runs\n"); + return -1; + } + + //ntfs_debug_runlist_dump2(runs, 5, "\t"); + + //ntfs_log_info(" VCN LCN Length\n"); + for (i = 0; runs[i].length > 0; i++) { + if (runs[i].lcn == LCN_RL_NOT_MAPPED) { + continue; + } + + res = move_datarun(vol, ino, rec, runs+i, loc, flags); + //ntfs_log_info(" %8lld %8lld %8lld\n", runs[i].vcn, runs[i].lcn, runs[i].length); + if (res < 0) { + ntfs_log_error("!move_datarun\n"); + count = res; + break; + } + count += res; + } + + return count; +} + +/** + * move_file - + * + * > 0 Bytes moved / size to be moved + * = 0 Nothing to do + * < 0 Error + */ +static s64 move_file(ntfs_volume *vol, ntfs_inode *ino, u64 loc, int flags) +{ + char *buffer; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *rec; + s64 res; + s64 count = 0; + + buffer = malloc(MAX_PATH); + if (!buffer) { + ntfs_log_error("Out of memory\n"); + return -1; + } + + utils_inode_get_name(ino, buffer, MAX_PATH); + + if (dont_move(ino)) { + ntfs_log_error("can't move\n"); + return -1; + } + + ntfs_log_info("Moving %s\n", buffer); + + // NTFS_MOVE_LOC_BEST : assess how much space all the attributes will need, + // find that space and pass the location to our children. + // Anything else we pass directly to move_attribute. + + ctx = ntfs_attr_get_search_ctx(ino, NULL); + + while ((rec = find_attribute(AT_UNUSED, ctx))) { + utils_attr_get_name(vol, rec, buffer, MAX_PATH); + ntfs_log_info("\tAttribute 0x%02x %s is ", rec->type, buffer); + + if (rec->non_resident) { + ntfs_log_info("non-resident. Moving it.\n"); + + res = move_attribute(vol, ino, rec, loc, flags); + if (res < 0) { + count = res; + break; + } + count += res; + } else { + ntfs_log_info("resident.\n\t\tSkipping it.\n"); + } + } + + ntfs_attr_put_search_ctx(ctx); + free(buffer); + return count; +} + + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + ntfs_volume *vol; + ntfs_inode *inode; + int flags = 0; + int result = 1; + s64 count; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + if (!parse_options(argc, argv)) + return 1; + + utils_set_locale(); + + if (opts.noaction) + flags |= NTFS_MNT_RDONLY; + if (opts.force) + flags |= NTFS_MNT_RECOVER; + + vol = utils_mount_volume(opts.device, flags); + if (!vol) { + ntfs_log_info("!vol\n"); + return 1; + } + + inode = ntfs_pathname_to_inode(vol, NULL, opts.file); + if (!inode) { + ntfs_log_info("!inode\n"); + return 1; + } + + count = move_file(vol, inode, opts.location, 0); + if ((count > 0) && (!opts.nodirty)) { + + /* Porting note: libntfs-3g does not automatically set or clear + * dirty flags on mount/unmount. It always preserves them until + * they are explicitly changed with ntfs_volume_write_flags. + * This means that the dirty flag is possibly not set, but + * should be set. So we explicitly set it with a call to + * ntfs_volume_write_flags. */ + if(!(vol->flags & VOLUME_IS_DIRTY) && ntfs_volume_write_flags( + vol, vol->flags | VOLUME_IS_DIRTY)) { + ntfs_log_error("Error: Failed to set volume dirty " + "flag (%d (%s))!\n", errno, strerror(errno)); + } + + ntfs_log_info("Relocated %lld bytes\n", (long long)count); + } + if (count >= 0) + result = 0; + + if (result) + ntfs_log_info("failed\n"); + else + ntfs_log_info("success\n"); + + ntfs_inode_close(inode); + ntfs_umount(vol, FALSE); + return result; +} diff --git a/ntfsprogs/ntfsmove.h b/ntfsprogs/ntfsmove.h new file mode 100755 index 0000000000000000000000000000000000000000..ffc1519a140b36c58ee6f845514a11277d5dd3d2 --- /dev/null +++ b/ntfsprogs/ntfsmove.h @@ -0,0 +1,46 @@ +/* + * ntfsmove - Part of the Linux-NTFS project. + * + * Copyright (c) 2003 Richard Russon + * + * This utility will move files on an NTFS volume. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFSMOVE_H_ +#define _NTFSMOVE_H_ + +#include "types.h" + /* Move files to */ +#define NTFS_MOVE_LOC_START -1000 /* the first available space */ +#define NTFS_MOVE_LOC_BEST -1001 /* place big enough for entire file */ +#define NTFS_MOVE_LOC_END -1002 /* the last available space */ + +struct options { + char *device; /* Device/File to work with */ + char *file; /* File to display */ + s64 location; /* Where to place the file */ + int force; /* Override common sense */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int noaction; /* Do not write to disk */ + int nodirty; /* Do not mark volume dirty */ +}; + +#endif /* _NTFSMOVE_H_ */ + + diff --git a/ntfsprogs/ntfsprogs.8 b/ntfsprogs/ntfsprogs.8 new file mode 100755 index 0000000000000000000000000000000000000000..43ed9ded75f5072fd2d8b2643b42da8e1a06a983 --- /dev/null +++ b/ntfsprogs/ntfsprogs.8 @@ -0,0 +1,75 @@ +.\" Copyright (c) 2002\-2005 Richard Russon. +.\" Copyright (c) 2002\-2003 Anton Altaparmakov. +.\" Copyright (c) 2005\-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2005\-2007 Yura Pakhuchiy. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSPROGS 8 "September 2007" "ntfs-3g 2015.3.14" +.SH NAME +ntfsprogs \- tools for doing neat things with NTFS +.SH OVERVIEW +.B ntfsprogs +is a suite of NTFS utilities based around a shared library. The tools are +available for free and come with full source code. +.SH TOOLS +.PP +.BR mkntfs (8) +\- Create an NTFS filesystem. +.PP +.BR ntfscat (8) +\- Dump a file's content to the standard output. +.PP +.BR ntfsclone (8) +\- Efficiently clone, backup, restore or rescue NTFS. +.PP +.BR ntfscluster (8) +\- Locate the files which use the given sectors or clusters. +.PP +.BR ntfscmp (8) +\- Compare two NTFS filesystems and tell the differences. +.PP +.BR ntfscp (8) +\- Copy a file to an NTFS volume. +.PP +.BR ntfsfix (8) +\- Check and fix some common errors, clear the LogFile and make Windows +perform a thorough check next time it boots. +.PP +.BR ntfsinfo (8) +\- Show information about NTFS or one of the files or directories within it. +.PP +.BR ntfslabel (8) +\- Show, or set, an NTFS filesystem's volume label. +.PP +.BR ntfsls (8) +\- List information about files in a directory residing on an NTFS. +.PP +.BR ntfsresize (8) +\- Resize NTFS without losing data. +.PP +.BR ntfstruncate (8) +\- Truncate a file on an NTFS volume. +.PP +.BR ntfsundelete (8) +\- Recover deleted files from NTFS. +.PP +.BR ntfswipe (8) +\- Overwrite unused space on an NTFS volume. +.SH AUTHORS +.PP +The tools were written by Anton Altaparmakov, Carmelo Kintana, Cristian Klein, +Erik Sornes, Giang Nguyen, Holger Ohmacht, Lode Leroy, Matthew J. Fanto, Per +Olofsson, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy and Yuval Fledel. +.SH AVAILABILITY +The +.B ntfsprogs +are part of the +.B ntfs-3g +package which can be downloaded from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfs\-3g (8) + diff --git a/ntfsprogs/ntfsprogs.8.in b/ntfsprogs/ntfsprogs.8.in new file mode 100755 index 0000000000000000000000000000000000000000..f00f5f493f16b6971bf55bf62b01bf788eaf102c --- /dev/null +++ b/ntfsprogs/ntfsprogs.8.in @@ -0,0 +1,75 @@ +.\" Copyright (c) 2002\-2005 Richard Russon. +.\" Copyright (c) 2002\-2003 Anton Altaparmakov. +.\" Copyright (c) 2005\-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2005\-2007 Yura Pakhuchiy. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSPROGS 8 "September 2007" "ntfs-3g @VERSION@" +.SH NAME +ntfsprogs \- tools for doing neat things with NTFS +.SH OVERVIEW +.B ntfsprogs +is a suite of NTFS utilities based around a shared library. The tools are +available for free and come with full source code. +.SH TOOLS +.PP +.BR mkntfs (8) +\- Create an NTFS filesystem. +.PP +.BR ntfscat (8) +\- Dump a file's content to the standard output. +.PP +.BR ntfsclone (8) +\- Efficiently clone, backup, restore or rescue NTFS. +.PP +.BR ntfscluster (8) +\- Locate the files which use the given sectors or clusters. +.PP +.BR ntfscmp (8) +\- Compare two NTFS filesystems and tell the differences. +.PP +.BR ntfscp (8) +\- Copy a file to an NTFS volume. +.PP +.BR ntfsfix (8) +\- Check and fix some common errors, clear the LogFile and make Windows +perform a thorough check next time it boots. +.PP +.BR ntfsinfo (8) +\- Show information about NTFS or one of the files or directories within it. +.PP +.BR ntfslabel (8) +\- Show, or set, an NTFS filesystem's volume label. +.PP +.BR ntfsls (8) +\- List information about files in a directory residing on an NTFS. +.PP +.BR ntfsresize (8) +\- Resize NTFS without losing data. +.PP +.BR ntfstruncate (8) +\- Truncate a file on an NTFS volume. +.PP +.BR ntfsundelete (8) +\- Recover deleted files from NTFS. +.PP +.BR ntfswipe (8) +\- Overwrite unused space on an NTFS volume. +.SH AUTHORS +.PP +The tools were written by Anton Altaparmakov, Carmelo Kintana, Cristian Klein, +Erik Sornes, Giang Nguyen, Holger Ohmacht, Lode Leroy, Matthew J. Fanto, Per +Olofsson, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy and Yuval Fledel. +.SH AVAILABILITY +The +.B ntfsprogs +are part of the +.B ntfs-3g +package which can be downloaded from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfs\-3g (8) + diff --git a/ntfsprogs/ntfsresize.8 b/ntfsprogs/ntfsresize.8 new file mode 100755 index 0000000000000000000000000000000000000000..389ce614a8cdd854f76afad0606e36235b411ae2 --- /dev/null +++ b/ntfsprogs/ntfsresize.8 @@ -0,0 +1,327 @@ +.\" Copyright (c) 2002\-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2005 Richard Russon. +.\" Copyright (c) 2011\-2013 Jean-Pierre Andre. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSRESIZE 8 "July 2013" "ntfs-3g 2015.3.14" +.SH NAME +ntfsresize \- resize an NTFS filesystem without data loss +.SH SYNOPSIS +.B ntfsresize +[\fIOPTIONS\fR] +.B \-\-info(\-mb\-only) +.I DEVICE +.br +.B ntfsresize +[\fIOPTIONS\fR] +[\fB\-\-size \fISIZE\fR[\fBk\fR|\fBM\fR|\fBG\fR]] +.I DEVICE +.SH DESCRIPTION +The +.B ntfsresize +program safely resizes Windows XP, Windows Server 2003, Windows 2000, Windows +NT4 and Longhorn NTFS filesystems without data loss. All NTFS versions are +supported, used by 32\-bit and 64\-bit Windows. +.B Defragmentation is NOT required prior to resizing +because the program can relocate any data if needed, without risking data +integrity. +.PP +Ntfsresize can be used to shrink or enlarge any NTFS filesystem located +on an unmounted +.I DEVICE +(usually a disk partition). The new filesystem will fit in a DEVICE +whose desired size is +.I SIZE +bytes. +The +.I SIZE +parameter may have one of the optional modifiers +.BR k , +.BR M , +.BR G , +which means the +.I SIZE +parameter is given in kilo\-, mega\- or gigabytes respectively. +.B Ntfsresize +conforms to the SI, ATA, IEEE standards and the disk manufacturers +by using k=10^3, M=10^6 and G=10^9. + +If both +.B \-\-info(\-mb\-only) +and +.B \-\-size +are omitted then the +NTFS filesystem will be enlarged to match the underlying +.I DEVICE +size. +.PP +To resize a filesystem on a partition, you must resize BOTH the filesystem +and the partition by editing the partition table on the disk. Similarly to +other command line filesystem resizers, +.B ntfsresize +doesn't manipulate the size of the partitions, hence +to do that you must use a disk partitioning tool as well, for example +.BR fdisk (8). +Alternatively you could use one of the many user friendly partitioners that +uses +.B ntfsresize +internally, like Mandriva's DiskDrake, QTParted, SUSE/Novell's YaST Partitioner, +IBM's EVMS, GParted or Debian/Ubuntu's Partman. +.PP +.B IMPORTANT! +It's a good practice making REGULAR BACKUPS of your valuable data, especially +before using ANY partitioning tools. To do so for NTFS, you could use +.BR ntfsclone (8). +Don't forget to save the partition table as well! +.SS Shrinkage +If you wish to shrink an NTFS partition, first use +.B ntfsresize +to shrink the size of the filesystem. Then you could use +.BR fdisk (8) +to shrink the size of the partition by deleting the +partition and recreating it with the smaller size. +Do not make the partition smaller than the new size of +NTFS otherwise you won't be able to boot. If you did so notwithstanding +then just recreate the partition to be as large as NTFS. +.SS Enlargement +To enlarge an NTFS filesystem, first you must enlarge the size of the +underlying partition. This can be done using +.BR fdisk (8) +by deleting the partition and recreating it with a larger size. +Make sure it will not overlap with an other existing partition. +You may enlarge upwards (first sector unchanged) or downwards (last +sector unchanged), but you may not enlarge at both ends in a single step. +If you merge two NTFS partitions, only one of them can be expanded to the +merged partition. +After you have enlarged the partition, you may use +.B ntfsresize +to enlarge the size of the filesystem. +.SS Partitioning +When recreating the partition by a disk partitioning tool, +make sure you create it at the same +starting sector and with the same partition type as before. +Otherwise you won't be able to access your filesystem. Use the 'u' +fdisk command to switch to the reliable sector unit from the +default cylinder one. + +Also make sure you set the bootable flag for the partition if it +existed before. Failing to do so you might not be able to boot your +computer from the disk. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsresize +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-c\fR, \fB\-\-check\fR +By using this option ntfsresize will only check the device to ensure that it +is ready to be resized. If not, it will print any errors detected. +If the device is fine, nothing will be printed. +.TP +\fB\-i\fR, \fB\-\-info\fR +By using this option without \fB\-\-expand\fP, ntfsresize will determine the +theoretically smallest shrunken filesystem size supported. +Most of the time the result is the space +already used on the filesystem. Ntfsresize will refuse shrinking to a +smaller size than what you got by this option and depending on several +factors it might be unable to shrink very close to this theoretical +size. Although the integrity of your data should be never in risk, +it's still strongly recommended to make a test run by using the +\fB\-\-no\-action\fR option before real resizing. + +Practically the smallest shrunken size generally is +at around "used space" + (20\-200 MB). Please also take into account +that Windows might need about 50\-100 MB free space left to boot safely. + +If used in association with option \fB\-\-expand\fP, ntfsresize will determine +the smallest downwards expansion size and the possible increments to the +size. These are exact byte counts which must not be rounded. +This option may be used after the partition has been expanded +provided the upper bound has not been changed. + +This option never causes any changes to the filesystem, the partition is +opened read\-only. +.TP +\fB\-m\fR, \fB\-\-info\-mb\-only\fR +Like the info option, only print out the shrinkable size in MB. Print nothing +if the shrink size is the same as the original size (in MB). +This option cannot be used in association with option \fB\-\-expand\fP. +.TP +\fB\-s\fR, \fB\-\-size\fR SIZE\fR[\fBk\fR|\fBM\fR|\fBG\fR] +Resize filesystem to fit in a partition whose size is +\fISIZE\fR[\fBk\fR|\fBM\fR|\fBG\fR] bytes by shifting its end and keeping +its beginning unchanged. The filesystem size is set to be at least one +sector smaller than the partition. +The optional modifiers +.BR k , +.BR M , +.B G +mean the +.I SIZE +parameter is given in kilo\-, mega\- or gigabytes respectively. +Conforming to standards, k=10^3, M=10^6 and G=10^9. ki=2^10, Mi=2^20 +and Gi=2^30 are also allowed. Use this option +with +.B \-\-no\-action +first. +.TP +\fB\-x\fR, \fB\-\-expand\fR +Expand the filesystem to the current partition size, shifting down its +beginning and keeping its end unchanged. The metadata is recreated in the +expanded space and no user data is relocated. This is incompatible with +option \-s (or \-\-size) and can only be made if the expanded space is an +exact multiple of the cluster size. It must also be large enough to hold the +new metadata. + +If the expansion is interrupted for some reason (power outage, etc), you may +restart the resizing, as the original data and metadata have been kept +unchanged. + +Note : expanding a Windows system partition and filesystem downwards may lead +to the registry or some files not matching the new system layout, or to +some important files being located too far from the beginning of the +partition, thus making Windows not bootable. +.TP +\fB\-f\fR, \fB\-\-force\fR +Forces ntfsresize to proceed with the resize operation either without +prompting for an explicit acceptance, or if the filesystem is marked for +consistency check. Double the option (-ff, --force --force) to avoid +prompting even if the file system is marked for check. + +Please note, ntfsresize always marks the filesystem +for consistency check before a real resize operation +and it leaves that way for extra +safety. Thus if NTFS was marked by ntfsresize then it's safe to +use this option. If you need +to resize several times without booting into Windows between each +resizing steps then you must use this option. +.TP +.B \-n, \-\-no\-action +Use this option to make a test run before doing the real resize operation. +Volume will be opened read\-only and +.B ntfsresize +displays what it would do if it were to resize the filesystem. +Continue with the real resizing only if the test run passed. +.TP +\fB\-b\fR, \fB\-\-bad\-sectors\fR +Support disks having hardware errors, bad sectors with those +.B ntfsresize +would refuse to work by default. + +Prior using this option, it's strongly recommended to make a backup by +.BR ntfsclone (8) +using the \-\-rescue option, then running 'chkdsk /f /r volume:' on Windows +from the command line. If the disk guarantee is still valid then replace it. +It's defected. Please also note, that no software can repair these type of +hardware errors. The most what they can do is to work around the permanent +defects. + +This option doesn't have any effect if the disk is flawless. +.TP +\fB\-P\fR, \fB\-\-no\-progress\-bar\fR +Don't show progress bars. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +More output. +.TP +\fB\-V\fR, \fB\-\-version\fR +Print the version number of +.B ntfsresize +and exit. +.TP +\fB\-h\fR, \fB\-\-help\fR +Display help and exit. +.SH EXIT CODES +The exit code is 0 on success, non\-zero otherwise. +.SH KNOWN ISSUES +No reliability problem is known. If you need +help please try the Ntfsresize FAQ first (see below) and if you +don't find your answer then send your question, comment or bug report to +the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.PP +There are a few very rarely met restrictions at present: filesystems having +unknown bad sectors, relocation +of the first MFT extent and resizing into the middle of a $MFTMirr extent +aren't supported yet. These cases are detected and +resizing is restricted to a safe size or the closest safe +size is displayed. +.PP +.B Ntfsresize +schedules an NTFS consistency check and +after the first boot into Windows you must see +.B chkdsk +running on a blue background. This is intentional and no need to worry about it. +Windows may force a quick reboot after the consistency check. +Moreover after repartitioning your disk and depending on the +hardware configuration, the Windows message +.B System Settings Change +may also appear. Just acknowledge it and reboot again. +.PP +The disk geometry handling semantic (HDIO_GETGEO ioctl) has changed +in an incompatible way in Linux 2.6 kernels and this triggered multitudinous +partition table corruptions resulting in unbootable Windows systems, even if +NTFS was consistent, if +.BR parted (8) +was involved in some way. This problem was often attributed to ntfsresize +but in fact it's completely independent of NTFS thus ntfsresize. Moreover +ntfsresize never touches the partition table at all. By changing +the 'Disk Access Mode' to LBA in the BIOS makes booting work +again, most of the time. You can find more information about this issue +in the Troubleshooting section of the below referred Ntfsresize FAQ. +.SH AUTHORS +.B ntfsresize +was written by Szabolcs Szakacsits, with contributions from Anton Altaparmakov +and Richard Russon. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH ACKNOWLEDGEMENT +Many thanks to Anton Altaparmakov and Richard Russon +for libntfs, the excellent documentation and comments, +to Gergely Madarasz, Dewey M. Sasser and Miguel Lastra and his colleagues +at the University of Granada for their continuous and highly valuable help, +furthermore to Erik Meade, Martin Fick, Sandro Hawke, Dave Croal, +Lorrin Nelson, Geert Hendrickx, Robert Bjorkman and Richard Burdick +for beta testing the relocation support, to Florian Eyben, Fritz Oppliger, +Richard Ebling, Sid\-Ahmed Touati, Jan Kiszka, Benjamin Redelings, Christopher +Haney, Ryan Durk, Ralf Beyer, Scott Hansen, Alan Evans for the valued +contributions and to Theodore Ts'o whose +.BR resize2fs (8) +man page originally formed the basis of this page. +.SH AVAILABILITY +.B ntfsresize +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.sp +.B Ntfsresize +related news, example of usage, troubleshooting, statically linked binary and +FAQ (frequently asked questions) are maintained at: +.br +.nh +http://mlf.linux.rulez.org/mlf/ezaz/ntfsresize.html +.hy +.SH SEE ALSO +.BR fdisk (8), +.BR cfdisk (8), +.BR sfdisk (8), +.BR parted (8), +.BR evms (8), +.BR ntfsclone (8), +.BR mkntfs (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsresize.8.in b/ntfsprogs/ntfsresize.8.in new file mode 100755 index 0000000000000000000000000000000000000000..177eb500b1b1accc65adb96afdbbca455d81c240 --- /dev/null +++ b/ntfsprogs/ntfsresize.8.in @@ -0,0 +1,327 @@ +.\" Copyright (c) 2002\-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2005 Richard Russon. +.\" Copyright (c) 2011\-2013 Jean-Pierre Andre. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSRESIZE 8 "July 2013" "ntfs-3g @VERSION@" +.SH NAME +ntfsresize \- resize an NTFS filesystem without data loss +.SH SYNOPSIS +.B ntfsresize +[\fIOPTIONS\fR] +.B \-\-info(\-mb\-only) +.I DEVICE +.br +.B ntfsresize +[\fIOPTIONS\fR] +[\fB\-\-size \fISIZE\fR[\fBk\fR|\fBM\fR|\fBG\fR]] +.I DEVICE +.SH DESCRIPTION +The +.B ntfsresize +program safely resizes Windows XP, Windows Server 2003, Windows 2000, Windows +NT4 and Longhorn NTFS filesystems without data loss. All NTFS versions are +supported, used by 32\-bit and 64\-bit Windows. +.B Defragmentation is NOT required prior to resizing +because the program can relocate any data if needed, without risking data +integrity. +.PP +Ntfsresize can be used to shrink or enlarge any NTFS filesystem located +on an unmounted +.I DEVICE +(usually a disk partition). The new filesystem will fit in a DEVICE +whose desired size is +.I SIZE +bytes. +The +.I SIZE +parameter may have one of the optional modifiers +.BR k , +.BR M , +.BR G , +which means the +.I SIZE +parameter is given in kilo\-, mega\- or gigabytes respectively. +.B Ntfsresize +conforms to the SI, ATA, IEEE standards and the disk manufacturers +by using k=10^3, M=10^6 and G=10^9. + +If both +.B \-\-info(\-mb\-only) +and +.B \-\-size +are omitted then the +NTFS filesystem will be enlarged to match the underlying +.I DEVICE +size. +.PP +To resize a filesystem on a partition, you must resize BOTH the filesystem +and the partition by editing the partition table on the disk. Similarly to +other command line filesystem resizers, +.B ntfsresize +doesn't manipulate the size of the partitions, hence +to do that you must use a disk partitioning tool as well, for example +.BR fdisk (8). +Alternatively you could use one of the many user friendly partitioners that +uses +.B ntfsresize +internally, like Mandriva's DiskDrake, QTParted, SUSE/Novell's YaST Partitioner, +IBM's EVMS, GParted or Debian/Ubuntu's Partman. +.PP +.B IMPORTANT! +It's a good practice making REGULAR BACKUPS of your valuable data, especially +before using ANY partitioning tools. To do so for NTFS, you could use +.BR ntfsclone (8). +Don't forget to save the partition table as well! +.SS Shrinkage +If you wish to shrink an NTFS partition, first use +.B ntfsresize +to shrink the size of the filesystem. Then you could use +.BR fdisk (8) +to shrink the size of the partition by deleting the +partition and recreating it with the smaller size. +Do not make the partition smaller than the new size of +NTFS otherwise you won't be able to boot. If you did so notwithstanding +then just recreate the partition to be as large as NTFS. +.SS Enlargement +To enlarge an NTFS filesystem, first you must enlarge the size of the +underlying partition. This can be done using +.BR fdisk (8) +by deleting the partition and recreating it with a larger size. +Make sure it will not overlap with an other existing partition. +You may enlarge upwards (first sector unchanged) or downwards (last +sector unchanged), but you may not enlarge at both ends in a single step. +If you merge two NTFS partitions, only one of them can be expanded to the +merged partition. +After you have enlarged the partition, you may use +.B ntfsresize +to enlarge the size of the filesystem. +.SS Partitioning +When recreating the partition by a disk partitioning tool, +make sure you create it at the same +starting sector and with the same partition type as before. +Otherwise you won't be able to access your filesystem. Use the 'u' +fdisk command to switch to the reliable sector unit from the +default cylinder one. + +Also make sure you set the bootable flag for the partition if it +existed before. Failing to do so you might not be able to boot your +computer from the disk. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsresize +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-c\fR, \fB\-\-check\fR +By using this option ntfsresize will only check the device to ensure that it +is ready to be resized. If not, it will print any errors detected. +If the device is fine, nothing will be printed. +.TP +\fB\-i\fR, \fB\-\-info\fR +By using this option without \fB\-\-expand\fP, ntfsresize will determine the +theoretically smallest shrunken filesystem size supported. +Most of the time the result is the space +already used on the filesystem. Ntfsresize will refuse shrinking to a +smaller size than what you got by this option and depending on several +factors it might be unable to shrink very close to this theoretical +size. Although the integrity of your data should be never in risk, +it's still strongly recommended to make a test run by using the +\fB\-\-no\-action\fR option before real resizing. + +Practically the smallest shrunken size generally is +at around "used space" + (20\-200 MB). Please also take into account +that Windows might need about 50\-100 MB free space left to boot safely. + +If used in association with option \fB\-\-expand\fP, ntfsresize will determine +the smallest downwards expansion size and the possible increments to the +size. These are exact byte counts which must not be rounded. +This option may be used after the partition has been expanded +provided the upper bound has not been changed. + +This option never causes any changes to the filesystem, the partition is +opened read\-only. +.TP +\fB\-m\fR, \fB\-\-info\-mb\-only\fR +Like the info option, only print out the shrinkable size in MB. Print nothing +if the shrink size is the same as the original size (in MB). +This option cannot be used in association with option \fB\-\-expand\fP. +.TP +\fB\-s\fR, \fB\-\-size\fR SIZE\fR[\fBk\fR|\fBM\fR|\fBG\fR] +Resize filesystem to fit in a partition whose size is +\fISIZE\fR[\fBk\fR|\fBM\fR|\fBG\fR] bytes by shifting its end and keeping +its beginning unchanged. The filesystem size is set to be at least one +sector smaller than the partition. +The optional modifiers +.BR k , +.BR M , +.B G +mean the +.I SIZE +parameter is given in kilo\-, mega\- or gigabytes respectively. +Conforming to standards, k=10^3, M=10^6 and G=10^9. ki=2^10, Mi=2^20 +and Gi=2^30 are also allowed. Use this option +with +.B \-\-no\-action +first. +.TP +\fB\-x\fR, \fB\-\-expand\fR +Expand the filesystem to the current partition size, shifting down its +beginning and keeping its end unchanged. The metadata is recreated in the +expanded space and no user data is relocated. This is incompatible with +option \-s (or \-\-size) and can only be made if the expanded space is an +exact multiple of the cluster size. It must also be large enough to hold the +new metadata. + +If the expansion is interrupted for some reason (power outage, etc), you may +restart the resizing, as the original data and metadata have been kept +unchanged. + +Note : expanding a Windows system partition and filesystem downwards may lead +to the registry or some files not matching the new system layout, or to +some important files being located too far from the beginning of the +partition, thus making Windows not bootable. +.TP +\fB\-f\fR, \fB\-\-force\fR +Forces ntfsresize to proceed with the resize operation either without +prompting for an explicit acceptance, or if the filesystem is marked for +consistency check. Double the option (-ff, --force --force) to avoid +prompting even if the file system is marked for check. + +Please note, ntfsresize always marks the filesystem +for consistency check before a real resize operation +and it leaves that way for extra +safety. Thus if NTFS was marked by ntfsresize then it's safe to +use this option. If you need +to resize several times without booting into Windows between each +resizing steps then you must use this option. +.TP +.B \-n, \-\-no\-action +Use this option to make a test run before doing the real resize operation. +Volume will be opened read\-only and +.B ntfsresize +displays what it would do if it were to resize the filesystem. +Continue with the real resizing only if the test run passed. +.TP +\fB\-b\fR, \fB\-\-bad\-sectors\fR +Support disks having hardware errors, bad sectors with those +.B ntfsresize +would refuse to work by default. + +Prior using this option, it's strongly recommended to make a backup by +.BR ntfsclone (8) +using the \-\-rescue option, then running 'chkdsk /f /r volume:' on Windows +from the command line. If the disk guarantee is still valid then replace it. +It's defected. Please also note, that no software can repair these type of +hardware errors. The most what they can do is to work around the permanent +defects. + +This option doesn't have any effect if the disk is flawless. +.TP +\fB\-P\fR, \fB\-\-no\-progress\-bar\fR +Don't show progress bars. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +More output. +.TP +\fB\-V\fR, \fB\-\-version\fR +Print the version number of +.B ntfsresize +and exit. +.TP +\fB\-h\fR, \fB\-\-help\fR +Display help and exit. +.SH EXIT CODES +The exit code is 0 on success, non\-zero otherwise. +.SH KNOWN ISSUES +No reliability problem is known. If you need +help please try the Ntfsresize FAQ first (see below) and if you +don't find your answer then send your question, comment or bug report to +the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.PP +There are a few very rarely met restrictions at present: filesystems having +unknown bad sectors, relocation +of the first MFT extent and resizing into the middle of a $MFTMirr extent +aren't supported yet. These cases are detected and +resizing is restricted to a safe size or the closest safe +size is displayed. +.PP +.B Ntfsresize +schedules an NTFS consistency check and +after the first boot into Windows you must see +.B chkdsk +running on a blue background. This is intentional and no need to worry about it. +Windows may force a quick reboot after the consistency check. +Moreover after repartitioning your disk and depending on the +hardware configuration, the Windows message +.B System Settings Change +may also appear. Just acknowledge it and reboot again. +.PP +The disk geometry handling semantic (HDIO_GETGEO ioctl) has changed +in an incompatible way in Linux 2.6 kernels and this triggered multitudinous +partition table corruptions resulting in unbootable Windows systems, even if +NTFS was consistent, if +.BR parted (8) +was involved in some way. This problem was often attributed to ntfsresize +but in fact it's completely independent of NTFS thus ntfsresize. Moreover +ntfsresize never touches the partition table at all. By changing +the 'Disk Access Mode' to LBA in the BIOS makes booting work +again, most of the time. You can find more information about this issue +in the Troubleshooting section of the below referred Ntfsresize FAQ. +.SH AUTHORS +.B ntfsresize +was written by Szabolcs Szakacsits, with contributions from Anton Altaparmakov +and Richard Russon. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH ACKNOWLEDGEMENT +Many thanks to Anton Altaparmakov and Richard Russon +for libntfs, the excellent documentation and comments, +to Gergely Madarasz, Dewey M. Sasser and Miguel Lastra and his colleagues +at the University of Granada for their continuous and highly valuable help, +furthermore to Erik Meade, Martin Fick, Sandro Hawke, Dave Croal, +Lorrin Nelson, Geert Hendrickx, Robert Bjorkman and Richard Burdick +for beta testing the relocation support, to Florian Eyben, Fritz Oppliger, +Richard Ebling, Sid\-Ahmed Touati, Jan Kiszka, Benjamin Redelings, Christopher +Haney, Ryan Durk, Ralf Beyer, Scott Hansen, Alan Evans for the valued +contributions and to Theodore Ts'o whose +.BR resize2fs (8) +man page originally formed the basis of this page. +.SH AVAILABILITY +.B ntfsresize +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.sp +.B Ntfsresize +related news, example of usage, troubleshooting, statically linked binary and +FAQ (frequently asked questions) are maintained at: +.br +.nh +http://mlf.linux.rulez.org/mlf/ezaz/ntfsresize.html +.hy +.SH SEE ALSO +.BR fdisk (8), +.BR cfdisk (8), +.BR sfdisk (8), +.BR parted (8), +.BR evms (8), +.BR ntfsclone (8), +.BR mkntfs (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c new file mode 100755 index 0000000000000000000000000000000000000000..ce3b9f5459658d0301414bd41e6064df44f3600d --- /dev/null +++ b/ntfsprogs/ntfsresize.c @@ -0,0 +1,4527 @@ +/** + * ntfsresize - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2002-2005 Anton Altaparmakov + * Copyright (c) 2002-2003 Richard Russon + * Copyright (c) 2007 Yura Pakhuchiy + * Copyright (c) 2011-2014 Jean-Pierre Andre + * + * This utility will resize an NTFS volume without data loss. + * + * WARNING FOR DEVELOPERS!!! Several external tools grep for text messages + * to control execution thus if you would like to change any message + * then PLEASE think twice before doing so then don't modify it. Thanks! + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include "debug.h" +#include "types.h" +#include "support.h" +#include "endians.h" +#include "bootsect.h" +#include "device.h" +#include "attrib.h" +#include "volume.h" +#include "mft.h" +#include "bitmap.h" +#include "inode.h" +#include "runlist.h" +#include "utils.h" +/* #include "version.h" */ +#include "misc.h" + +#define BAN_NEW_TEXT 1 /* Respect the ban on new messages */ +#define CLEAN_EXIT 0 /* traditionnally volume is not closed, there must be a reason */ + +static const char *EXEC_NAME = "ntfsresize"; + +static const char *resize_warning_msg = +"WARNING: Every sanity check passed and only the dangerous operations left.\n" +"Make sure that important data has been backed up! Power outage or computer\n" +"crash may result major data loss!\n"; + +static const char *resize_important_msg = +"You can go on to shrink the device for example with Linux fdisk.\n" +"IMPORTANT: When recreating the partition, make sure that you\n" +" 1) create it at the same disk sector (use sector as the unit!)\n" +" 2) create it with the same partition type (usually 7, HPFS/NTFS)\n" +" 3) do not make it smaller than the new NTFS filesystem size\n" +" 4) set the bootable flag for the partition if it existed before\n" +"Otherwise you won't be able to access NTFS or can't boot from the disk!\n" +"If you make a mistake and don't have a partition table backup then you\n" +"can recover the partition table by TestDisk or Parted's rescue mode.\n"; + +static const char *invalid_ntfs_msg = +"The device '%s' doesn't have a valid NTFS.\n" +"Maybe you selected the wrong partition? Or the whole disk instead of a\n" +"partition (e.g. /dev/hda, not /dev/hda1)? This error might also occur\n" +"if the disk was incorrectly repartitioned (see the ntfsresize FAQ).\n"; + +static const char *corrupt_volume_msg = +"NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n" +"The usage of the /f parameter is very IMPORTANT! No modification was\n" +"and will be made to NTFS by this software until it gets repaired.\n"; + +static const char *hibernated_volume_msg = +"The NTFS partition is hibernated. Windows must be resumed and turned off\n" +"properly, so resizing could be done safely.\n"; + +static const char *unclean_journal_msg = +"The NTFS journal file is unclean. Please shutdown Windows properly before\n" +"using this software! Note, if you have run chkdsk previously then boot\n" +"Windows again which will automatically initialize the journal correctly.\n"; + +static const char *opened_volume_msg = +"This software has detected that the NTFS volume is already opened by another\n" +"software thus it refuses to progress to preserve data consistency.\n"; + +static const char *bad_sectors_warning_msg = +"****************************************************************************\n" +"* WARNING: The disk has bad sector. This means physical damage on the disk *\n" +"* surface caused by deterioration, manufacturing faults or other reason. *\n" +"* The reliability of the disk may stay stable or degrade fast. We suggest *\n" +"* making a full backup urgently by running 'ntfsclone --rescue ...' then *\n" +"* run 'chkdsk /f /r' on Windows and rebooot it TWICE! Then you can resize *\n" +"* NTFS safely by additionally using the --bad-sectors option of ntfsresize.*\n" +"****************************************************************************\n"; + +static const char *many_bad_sectors_msg = +"***************************************************************************\n" +"* WARNING: The disk has many bad sectors. This means physical damage *\n" +"* on the disk surface caused by deterioration, manufacturing faults or *\n" +"* other reason. We suggest to get a replacement disk as soon as possible. *\n" +"***************************************************************************\n"; + +static struct { + int verbose; + int debug; + int ro_flag; + int force; + int info; + int infombonly; + int expand; + int reliable_size; + int show_progress; + int badsectors; + int check; + s64 bytes; + char *volume; +} opt; + +struct bitmap { + s64 size; + u8 *bm; +}; + +#define NTFS_PROGBAR 0x0001 +#define NTFS_PROGBAR_SUPPRESS 0x0002 + +struct progress_bar { + u64 start; + u64 stop; + int resolution; + int flags; + float unit; +}; + +struct llcn_t { + s64 lcn; /* last used LCN for a "special" file/attr type */ + s64 inode; /* inode using it */ +}; + +#define NTFSCK_PROGBAR 0x0001 + + /* runlists which have to be processed later */ +struct DELAYED { + struct DELAYED *next; + ATTR_TYPES type; + MFT_REF mref; + VCN lowest_vcn; + int name_len; + ntfschar *attr_name; + runlist_element *rl; + runlist *head_rl; +} ; + +typedef struct { + ntfs_inode *ni; /* inode being processed */ + ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ + s64 inuse; /* num of clusters in use */ + int multi_ref; /* num of clusters referenced many times */ + int outsider; /* num of clusters outside the volume */ + int show_outsider; /* controls showing the above information */ + int flags; + struct bitmap lcn_bitmap; +} ntfsck_t; + +typedef struct { + ntfs_volume *vol; + ntfs_inode *ni; /* inode being processed */ + s64 new_volume_size; /* in clusters; 0 = --info w/o --size */ + MFT_REF mref; /* mft reference */ + MFT_RECORD *mrec; /* mft record */ + ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ + u64 relocations; /* num of clusters to relocate */ + s64 inuse; /* num of clusters in use */ + runlist mftmir_rl; /* $MFTMirr AT_DATA's new position */ + s64 mftmir_old; /* $MFTMirr AT_DATA's old LCN */ + int dirty_inode; /* some inode data got relocated */ + int shrink; /* shrink = 1, enlarge = 0 */ + s64 badclusters; /* num of physically dead clusters */ + VCN mft_highest_vcn; /* used for relocating the $MFT */ + runlist_element *new_mft_start; /* new first run for $MFT:$DATA */ + struct DELAYED *delayed_runlists; /* runlists to process later */ + struct progress_bar progress; + struct bitmap lcn_bitmap; + /* Temporary statistics until all case is supported */ + struct llcn_t last_mft; + struct llcn_t last_mftmir; + struct llcn_t last_multi_mft; + struct llcn_t last_sparse; + struct llcn_t last_compressed; + struct llcn_t last_lcn; + s64 last_unsupp; /* last unsupported cluster */ +} ntfs_resize_t; + +/* FIXME: This, lcn_bitmap and pos from find_free_cluster() will make a cluster + allocation related structure, attached to ntfs_resize_t */ +static s64 max_free_cluster_range = 0; + +#define NTFS_MBYTE (1000 * 1000) + +/* WARNING: don't modify the text, external tools grep for it */ +#define ERR_PREFIX "ERROR" +#define PERR_PREFIX ERR_PREFIX "(%d): " +#define NERR_PREFIX ERR_PREFIX ": " + +#define DIRTY_NONE (0) +#define DIRTY_INODE (1) +#define DIRTY_ATTRIB (2) + +#define NTFS_MAX_CLUSTER_SIZE (65536) + +static s64 rounded_up_division(s64 numer, s64 denom) +{ + return (numer + (denom - 1)) / denom; +} + +/** + * perr_printf + * + * Print an error message. + */ +__attribute__((format(printf, 1, 2))) +static void perr_printf(const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + fprintf(stdout, PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fprintf(stdout, ": %s\n", strerror(eo)); + fflush(stdout); + fflush(stderr); +} + +__attribute__((format(printf, 1, 2))) +static void err_printf(const char *fmt, ...) +{ + va_list ap; + + fprintf(stdout, NERR_PREFIX); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fflush(stdout); + fflush(stderr); +} + +/** + * err_exit + * + * Print and error message and exit the program. + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static void err_exit(const char *fmt, ...) +{ + va_list ap; + + fprintf(stdout, NERR_PREFIX); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fflush(stdout); + fflush(stderr); + exit(1); +} + +/** + * perr_exit + * + * Print and error message and exit the program + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static void perr_exit(const char *fmt, ...) +{ + va_list ap; + int eo = errno; + + fprintf(stdout, PERR_PREFIX, eo); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + printf(": %s\n", strerror(eo)); + fflush(stdout); + fflush(stderr); + exit(1); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +__attribute__((noreturn)) +static void usage(int ret) +{ + + printf("\nUsage: %s [OPTIONS] DEVICE\n" + " Resize an NTFS volume non-destructively, safely move any data if needed.\n" + "\n" + " -c, --check Check to ensure that the device is ready for resize\n" + " -i, --info Estimate the smallest shrunken size or the smallest\n" + " expansion size\n" + " -m, --info-mb-only Estimate the smallest shrunken size possible,\n" + " output size in MB only\n" + " -s, --size SIZE Resize volume to SIZE[k|M|G] bytes\n" + " -x, --expand Expand to full partition\n" + "\n" + " -n, --no-action Do not write to disk\n" + " -b, --bad-sectors Support disks having bad sectors\n" + " -f, --force Force to progress\n" + " -P, --no-progress-bar Don't show progress bar\n" + " -v, --verbose More output\n" + " -V, --version Display version information\n" + " -h, --help Display this help\n" +#ifdef DEBUG + " -d, --debug Show debug information\n" +#endif + "\n" + " The options -i and -x are exclusive of option -s, and -m is exclusive\n" + " of option -x. If options -i, -m, -s and -x are are all omitted\n" + " then the NTFS volume will be enlarged to the DEVICE size.\n" + "\n", EXEC_NAME); + printf("%s%s", ntfs_bugs, ntfs_home); + printf("Ntfsresize FAQ: http://linux-ntfs.sourceforge.net/info/ntfsresize.html\n"); + exit(ret); +} + +/** + * proceed_question + * + * Force the user to confirm an action before performing it. + * Copy-paste from e2fsprogs + */ +static void proceed_question(void) +{ + char buf[256]; + const char *short_yes = "yY"; + + fflush(stdout); + fflush(stderr); + printf("Are you sure you want to proceed (y/[n])? "); + buf[0] = 0; + if (fgets(buf, sizeof(buf), stdin) + && !strchr(short_yes, buf[0])) { + printf("OK quitting. NO CHANGES have been made to your " + "NTFS volume.\n"); + exit(1); + } +} + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + printf("\nResize an NTFS Volume, without data loss.\n\n"); + printf("Copyright (c) 2002-2006 Szabolcs Szakacsits\n"); + printf("Copyright (c) 2002-2005 Anton Altaparmakov\n"); + printf("Copyright (c) 2002-2003 Richard Russon\n"); + printf("Copyright (c) 2007 Yura Pakhuchiy\n"); + printf("Copyright (c) 2011-2014 Jean-Pierre Andre\n"); + printf("\n%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * get_new_volume_size + * + * Convert a user-supplied string into a size. Without any suffix the number + * will be assumed to be in bytes. If the number has a suffix of k, M or G it + * will be scaled up by 1000, 1000000, or 1000000000. + */ +static s64 get_new_volume_size(char *s) +{ + s64 size; + char *suffix; + int prefix_kind = 1000; + + size = strtoll(s, &suffix, 10); + if (size <= 0 || errno == ERANGE) + err_exit("Illegal new volume size\n"); + + if (!*suffix) { + opt.reliable_size = 1; + return size; + } + + if (strlen(suffix) == 2 && suffix[1] == 'i') + prefix_kind = 1024; + else if (strlen(suffix) > 1) + usage(1); + + /* We follow the SI prefixes: + http://physics.nist.gov/cuu/Units/prefixes.html + http://physics.nist.gov/cuu/Units/binary.html + Disk partitioning tools use prefixes as, + k M G + fdisk 2.11x- 2^10 2^20 10^3*2^20 + fdisk 2.11y+ 10^3 10^6 10^9 + cfdisk 10^3 10^6 10^9 + sfdisk 2^10 2^20 + parted 2^10 2^20 (may change) + fdisk (DOS) 2^10 2^20 + */ + /* FIXME: check for overflow */ + switch (*suffix) { + case 'G': + size *= prefix_kind; + case 'M': + size *= prefix_kind; + case 'k': + size *= prefix_kind; + break; + default: + usage(1); + } + + return size; +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char **argv) +{ + static const char *sopt = "-bcdfhimnPs:vVx"; + static const struct option lopt[] = { + { "bad-sectors",no_argument, NULL, 'b' }, + { "check", no_argument, NULL, 'c' }, +#ifdef DEBUG + { "debug", no_argument, NULL, 'd' }, +#endif + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "info", no_argument, NULL, 'i' }, + { "info-mb-only", no_argument, NULL, 'm' }, + { "no-action", no_argument, NULL, 'n' }, + { "no-progress-bar", no_argument, NULL, 'P' }, + { "size", required_argument, NULL, 's' }, + { "expand", no_argument, NULL, 'x' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c; + int err = 0; + int ver = 0; + int help = 0; + + memset(&opt, 0, sizeof(opt)); + opt.show_progress = 1; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!err && !opt.volume) + opt.volume = argv[optind-1]; + else + err++; + break; + case 'b': + opt.badsectors++; + break; + case 'c': + opt.check++; + break; + case 'd': + opt.debug++; + break; + case 'f': + opt.force++; + break; + case 'h': + help++; + break; + case 'i': + opt.info++; + break; + case 'm': + opt.infombonly++; + break; + case 'n': + opt.ro_flag = NTFS_MNT_RDONLY; + break; + case 'P': + opt.show_progress = 0; + break; + case 's': + if (!err && (opt.bytes == 0)) + opt.bytes = get_new_volume_size(optarg); + else + err++; + break; + case 'v': + opt.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + case 'x': + opt.expand++; + break; + case '?': + default: + if (optopt == 's') { + printf("Option '%s' requires an argument.\n", argv[optind-1]); + } else { + printf("Unknown option '%s'.\n", argv[optind-1]); + } + err++; + break; + } + } + + if (!help && !ver) { + if (opt.volume == NULL) { + if (argc > 1) + printf("You must specify exactly one device.\n"); + err++; + } + if (opt.info || opt.infombonly) { + opt.ro_flag = NTFS_MNT_RDONLY; + } + if (opt.bytes + && (opt.expand || opt.info || opt.infombonly)) { + printf(NERR_PREFIX "Options --info(-mb-only) and --expand " + "cannot be used with --size.\n"); + usage(1); + } + if (opt.expand && opt.infombonly) { + printf(NERR_PREFIX "Options --info-mb-only " + "cannot be used with --expand.\n"); + usage(1); + } + } + + /* Redirect stderr to stdout, note fflush()es are essential! */ + fflush(stdout); + fflush(stderr); + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) + perr_exit("Failed to redirect stderr to stdout"); + fflush(stdout); + fflush(stderr); + +#ifdef DEBUG + if (!opt.debug) + if (!freopen("/dev/null", "w", stderr)) + perr_exit("Failed to redirect stderr to /dev/null"); +#endif + + if (ver) + version(); + if (help || err) + usage(err > 0); + + /* tri-state 0 : done, 1 : error, -1 : proceed */ + return (err ? 1 : (help || ver ? 0 : -1)); +} + +static void print_advise(ntfs_volume *vol, s64 supp_lcn) +{ + s64 old_b, new_b, freed_b, old_mb, new_mb, freed_mb; + + old_b = vol->nr_clusters * vol->cluster_size; + old_mb = rounded_up_division(old_b, NTFS_MBYTE); + + /* Take the next supported cluster (free or relocatable) + plus reserve a cluster for the backup boot sector */ + supp_lcn += 2; + + if (supp_lcn > vol->nr_clusters) { + err_printf("Very rare fragmentation type detected. " + "Sorry, it's not supported yet.\n" + "Try to defragment your NTFS, perhaps it helps.\n"); + exit(1); + } + + new_b = supp_lcn * vol->cluster_size; + new_mb = rounded_up_division(new_b, NTFS_MBYTE); + freed_b = (vol->nr_clusters - supp_lcn + 1) * vol->cluster_size; + freed_mb = freed_b / NTFS_MBYTE; + + /* WARNING: don't modify the text, external tools grep for it */ + if (!opt.infombonly) + printf("You might resize at %lld bytes ", (long long)new_b); + if ((new_mb * NTFS_MBYTE) < old_b) { + if (!opt.infombonly) + printf("or %lld MB ", (long long)new_mb); + else + printf("Minsize (in MB): %lld\n", (long long)new_mb); + } + + if (!opt.infombonly) { + printf("(freeing "); + if (freed_mb && (old_mb - new_mb)) + printf("%lld MB", (long long)(old_mb - new_mb)); + else + printf("%lld bytes", (long long)freed_b); + printf(").\n"); + + printf("Please make a test run using both the -n and -s " + "options before real resizing!\n"); + } +} + +static void rl_set(runlist *rl, VCN vcn, LCN lcn, s64 len) +{ + rl->vcn = vcn; + rl->lcn = lcn; + rl->length = len; +} + +static int rl_items(runlist *rl) +{ + int i = 0; + + while (rl[i++].length) + ; + + return i; +} + +static void dump_run(runlist_element *r) +{ + ntfs_log_verbose(" %8lld %8lld (0x%08llx) %lld\n", (long long)r->vcn, + (long long)r->lcn, (long long)r->lcn, + (long long)r->length); +} + +static void dump_runlist(runlist *rl) +{ + while (rl->length) + dump_run(rl++); +} + +/** + * nr_clusters_to_bitmap_byte_size + * + * Take the number of clusters in the volume and calculate the size of $Bitmap. + * The size must be always a multiple of 8 bytes. + */ +static s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters) +{ + s64 bm_bsize; + + bm_bsize = rounded_up_division(nr_clusters, 8); + bm_bsize = (bm_bsize + 7) & ~7; + + return bm_bsize; +} + +static void collect_resize_constraints(ntfs_resize_t *resize, runlist *rl) +{ + s64 inode, last_lcn; + ATTR_FLAGS flags; + ATTR_TYPES atype; + struct llcn_t *llcn = NULL; + int ret, supported = 0; + + last_lcn = rl->lcn + (rl->length - 1); + + inode = resize->ni->mft_no; + flags = resize->ctx->attr->flags; + atype = resize->ctx->attr->type; + + if ((ret = ntfs_inode_badclus_bad(inode, resize->ctx->attr)) != 0) { + if (ret == -1) + perr_exit("Bad sector list check failed"); + return; + } + + if (inode == FILE_Bitmap) { + llcn = &resize->last_lcn; + if (atype == AT_DATA && NInoAttrList(resize->ni)) + err_exit("Highly fragmented $Bitmap isn't supported yet."); + + supported = 1; + + } else if (NInoAttrList(resize->ni)) { + llcn = &resize->last_multi_mft; + + if (inode != FILE_MFTMirr) + supported = 1; + + } else if (flags & ATTR_IS_SPARSE) { + llcn = &resize->last_sparse; + supported = 1; + + } else if (flags & ATTR_IS_COMPRESSED) { + llcn = &resize->last_compressed; + supported = 1; + + } else if (inode == FILE_MFTMirr) { + llcn = &resize->last_mftmir; + supported = 1; + + /* Fragmented $MFTMirr DATA attribute isn't supported yet */ + if (atype == AT_DATA) + if (rl[1].length != 0 || rl->vcn) + supported = 0; + } else { + llcn = &resize->last_lcn; + supported = 1; + } + + if (llcn->lcn < last_lcn) { + llcn->lcn = last_lcn; + llcn->inode = inode; + } + + if (supported) + return; + + if (resize->last_unsupp < last_lcn) + resize->last_unsupp = last_lcn; +} + + +static void collect_relocation_info(ntfs_resize_t *resize, runlist *rl) +{ + s64 lcn, lcn_length, start, len, inode; + s64 new_vol_size; /* (last LCN on the volume) + 1 */ + + lcn = rl->lcn; + lcn_length = rl->length; + inode = resize->ni->mft_no; + new_vol_size = resize->new_volume_size; + + if (lcn + lcn_length <= new_vol_size) + return; + + if (inode == FILE_Bitmap && resize->ctx->attr->type == AT_DATA) + return; + + start = lcn; + len = lcn_length; + + if (lcn < new_vol_size) { + start = new_vol_size; + len = lcn_length - (new_vol_size - lcn); + + if ((!opt.info && !opt.infombonly) && (inode == FILE_MFTMirr)) { + err_printf("$MFTMirr can't be split up yet. Please try " + "a different size.\n"); + print_advise(resize->vol, lcn + lcn_length - 1); + exit(1); + } + } + + resize->relocations += len; + + if ((!opt.info && !opt.infombonly) || !resize->new_volume_size) + return; + + printf("Relocation needed for inode %8lld attr 0x%x LCN 0x%08llx " + "length %6lld\n", (long long)inode, + (unsigned int)le32_to_cpu(resize->ctx->attr->type), + (unsigned long long)start, (long long)len); +} + +/** + * build_lcn_usage_bitmap + * + * lcn_bitmap has one bit for each cluster on the disk. Initially, lcn_bitmap + * has no bits set. As each attribute record is read the bits in lcn_bitmap are + * checked to ensure that no other file already references that cluster. + * + * This serves as a rudimentary "chkdsk" operation. + */ +static void build_lcn_usage_bitmap(ntfs_volume *vol, ntfsck_t *fsck) +{ + s64 inode; + ATTR_RECORD *a; + runlist *rl; + int i, j; + struct bitmap *lcn_bitmap = &fsck->lcn_bitmap; + + a = fsck->ctx->attr; + inode = fsck->ni->mft_no; + + if (!a->non_resident) + return; + + if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) { + int err = errno; + perr_printf("ntfs_decompress_mapping_pairs"); + if (err == EIO) + printf("%s", corrupt_volume_msg); + exit(1); + } + + + for (i = 0; rl[i].length; i++) { + s64 lcn = rl[i].lcn; + s64 lcn_length = rl[i].length; + + /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ + if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) + continue; + + /* FIXME: ntfs_mapping_pairs_decompress should return error */ + if (lcn < 0 || lcn_length <= 0) + err_exit("Corrupt runlist in inode %lld attr %x LCN " + "%llx length %llx\n", (long long)inode, + (unsigned int)le32_to_cpu(a->type), + (long long)lcn, (long long)lcn_length); + + for (j = 0; j < lcn_length; j++) { + u64 k = (u64)lcn + j; + + if (k >= (u64)vol->nr_clusters) { + long long outsiders = lcn_length - j; + + fsck->outsider += outsiders; + + if (++fsck->show_outsider <= 10 || opt.verbose) + printf("Outside of the volume reference" + " for inode %lld at %lld:%lld\n", + (long long)inode, (long long)k, + (long long)outsiders); + + break; + } + + if (ntfs_bit_get_and_set(lcn_bitmap->bm, k, 1)) { + if (++fsck->multi_ref <= 10 || opt.verbose) + printf("Cluster %lld is referenced " + "multiple times!\n", + (long long)k); + continue; + } + } + fsck->inuse += lcn_length; + } + free(rl); +} + + +static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) +{ + ntfs_attr_search_ctx *ret; + + if ((ret = ntfs_attr_get_search_ctx(ni, mrec)) == NULL) + perr_printf("ntfs_attr_get_search_ctx"); + + return ret; +} + +/** + * walk_attributes + * + * For a given MFT Record, iterate through all its attributes. Any non-resident + * data runs will be marked in lcn_bitmap. + */ +static int walk_attributes(ntfs_volume *vol, ntfsck_t *fsck) +{ + if (!(fsck->ctx = attr_get_search_ctx(fsck->ni, NULL))) + return -1; + + while (!ntfs_attrs_walk(fsck->ctx)) { + if (fsck->ctx->attr->type == AT_END) + break; + build_lcn_usage_bitmap(vol, fsck); + } + + ntfs_attr_put_search_ctx(fsck->ctx); + return 0; +} + +/** + * compare_bitmaps + * + * Compare two bitmaps. In this case, $Bitmap as read from the disk and + * lcn_bitmap which we built from the MFT Records. + */ +static void compare_bitmaps(ntfs_volume *vol, struct bitmap *a) +{ + s64 i, pos, count; + int mismatch = 0; + int backup_boot = 0; + u8 bm[NTFS_BUF_SIZE]; + + if (!opt.infombonly) + printf("Accounting clusters ...\n"); + + pos = 0; + while (1) { + count = ntfs_attr_pread(vol->lcnbmp_na, pos, NTFS_BUF_SIZE, bm); + if (count == -1) + perr_exit("Couldn't get $Bitmap $DATA"); + + if (count == 0) { + if (a->size > pos) + err_exit("$Bitmap size is smaller than expected" + " (%lld != %lld)\n", + (long long)a->size, (long long)pos); + break; + } + + for (i = 0; i < count; i++, pos++) { + s64 cl; /* current cluster */ + + if (a->size <= pos) + goto done; + + if (a->bm[pos] == bm[i]) + continue; + + for (cl = pos * 8; cl < (pos + 1) * 8; cl++) { + char bit; + + bit = ntfs_bit_get(a->bm, cl); + if (bit == ntfs_bit_get(bm, i * 8 + cl % 8)) + continue; + + if (!mismatch && !bit && !backup_boot && + cl == vol->nr_clusters / 2) { + /* FIXME: call also boot sector check */ + backup_boot = 1; + printf("Found backup boot sector in " + "the middle of the volume.\n"); + continue; + } + + if (++mismatch > 10 && !opt.verbose) + continue; + + printf("Cluster accounting failed at %lld " + "(0x%llx): %s cluster in " + "$Bitmap\n", (long long)cl, + (unsigned long long)cl, + bit ? "missing" : "extra"); + } + } + } +done: + if (mismatch) { + printf("Filesystem check failed! Totally %d cluster " + "accounting mismatches.\n", mismatch); + err_printf("%s", corrupt_volume_msg); + exit(1); + } +} + +/** + * progress_init + * + * Create and scale our progress bar. + */ +static void progress_init(struct progress_bar *p, u64 start, u64 stop, int flags) +{ + p->start = start; + p->stop = stop; + p->unit = 100.0 / (stop - start); + p->resolution = 100; + p->flags = flags; +} + +/** + * progress_update + * + * Update the progress bar and tell the user. + */ +static void progress_update(struct progress_bar *p, u64 current) +{ + float percent; + + if (!(p->flags & NTFS_PROGBAR)) + return; + if (p->flags & NTFS_PROGBAR_SUPPRESS) + return; + + /* WARNING: don't modify the texts, external tools grep for them */ + percent = p->unit * current; + if (current != p->stop) { + if ((current - p->start) % p->resolution) + return; + printf("%6.2f percent completed\r", percent); + } else + printf("100.00 percent completed\n"); + fflush(stdout); +} + +static int inode_close(ntfs_inode *ni) +{ + if (ntfs_inode_close(ni)) { + perr_printf("ntfs_inode_close for inode %llu", + (unsigned long long)ni->mft_no); + return -1; + } + return 0; +} + +/** + * walk_inodes + * + * Read each record in the MFT, skipping the unused ones, and build up a bitmap + * from all the non-resident attributes. + */ +static int build_allocation_bitmap(ntfs_volume *vol, ntfsck_t *fsck) +{ + s64 nr_mft_records, inode = 0; + ntfs_inode *ni; + struct progress_bar progress; + int pb_flags = 0; /* progress bar flags */ + + /* WARNING: don't modify the text, external tools grep for it */ + if (!opt.infombonly) + printf("Checking filesystem consistency ...\n"); + + if (fsck->flags & NTFSCK_PROGBAR) + pb_flags |= NTFS_PROGBAR; + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + progress_init(&progress, inode, nr_mft_records - 1, pb_flags); + + for (; inode < nr_mft_records; inode++) { + if (!opt.infombonly) + progress_update(&progress, inode); + + if ((ni = ntfs_inode_open(vol, (MFT_REF)inode)) == NULL) { + /* FIXME: continue only if it make sense, e.g. + MFT record not in use based on $MFT bitmap */ + if (errno == EIO || errno == ENOENT) + continue; + perr_printf("Reading inode %lld failed", + (long long)inode); + return -1; + } + + if (ni->mrec->base_mft_record) + goto close_inode; + + fsck->ni = ni; + if (walk_attributes(vol, fsck) != 0) { + inode_close(ni); + return -1; + } +close_inode: + if (inode_close(ni) != 0) + return -1; + } + return 0; +} + +static void build_resize_constraints(ntfs_resize_t *resize) +{ + s64 i; + runlist *rl; + + if (!resize->ctx->attr->non_resident) + return; + + if (!(rl = ntfs_mapping_pairs_decompress(resize->vol, + resize->ctx->attr, NULL))) + perr_exit("ntfs_decompress_mapping_pairs"); + + for (i = 0; rl[i].length; i++) { + /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ + if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED) + continue; + + collect_resize_constraints(resize, rl + i); + if (resize->shrink) + collect_relocation_info(resize, rl + i); + } + free(rl); +} + +static void resize_constraints_by_attributes(ntfs_resize_t *resize) +{ + if (!(resize->ctx = attr_get_search_ctx(resize->ni, NULL))) + exit(1); + + while (!ntfs_attrs_walk(resize->ctx)) { + if (resize->ctx->attr->type == AT_END) + break; + build_resize_constraints(resize); + } + + ntfs_attr_put_search_ctx(resize->ctx); +} + +static void set_resize_constraints(ntfs_resize_t *resize) +{ + s64 nr_mft_records, inode; + ntfs_inode *ni; + + if (!opt.infombonly) + printf("Collecting resizing constraints ...\n"); + + nr_mft_records = resize->vol->mft_na->initialized_size >> + resize->vol->mft_record_size_bits; + + for (inode = 0; inode < nr_mft_records; inode++) { + + ni = ntfs_inode_open(resize->vol, (MFT_REF)inode); + if (ni == NULL) { + if (errno == EIO || errno == ENOENT) + continue; + perr_exit("Reading inode %lld failed", + (long long)inode); + } + + if (ni->mrec->base_mft_record) + goto close_inode; + + resize->ni = ni; + resize_constraints_by_attributes(resize); +close_inode: + if (inode_close(ni) != 0) + exit(1); + } +} + +static void rl_fixup(runlist **rl) +{ + runlist *tmp = *rl; + + if (tmp->lcn == LCN_RL_NOT_MAPPED) { + s64 unmapped_len = tmp->length; + + ntfs_log_verbose("Skip unmapped run at the beginning ...\n"); + + if (!tmp->length) + err_exit("Empty unmapped runlist! Please report!\n"); + (*rl)++; + for (tmp = *rl; tmp->length; tmp++) + tmp->vcn -= unmapped_len; + } + + for (tmp = *rl; tmp->length; tmp++) { + if (tmp->lcn == LCN_RL_NOT_MAPPED) { + ntfs_log_verbose("Skip unmapped run at the end ...\n"); + + if (tmp[1].length) + err_exit("Unmapped runlist in the middle! " + "Please report!\n"); + tmp->lcn = LCN_ENOENT; + tmp->length = 0; + } + } +} + +/* + * Plug a replacement (partial) runlist into full runlist + * + * Returns 0 if successful + * -1 if failed + */ + +static int replace_runlist(ntfs_attr *na, const runlist_element *reprl, + VCN lowest_vcn) +{ + const runlist_element *prep; + const runlist_element *pold; + runlist_element *pnew; + runlist_element *newrl; + VCN nextvcn; + s32 oldcnt, newcnt; + s32 newsize; + int r; + + r = -1; /* default return */ + /* allocate a new runlist able to hold both */ + oldcnt = 0; + while (na->rl[oldcnt].length) + oldcnt++; + newcnt = 0; + while (reprl[newcnt].length) + newcnt++; + newsize = ((oldcnt + newcnt)*sizeof(runlist_element) + 4095) & -4096; + newrl = (runlist_element*)malloc(newsize); + if (newrl) { + /* copy old runs until reaching replaced ones */ + pnew = newrl; + pold = na->rl; + while (pold->length + && ((pold->vcn + pold->length) + <= (reprl[0].vcn + lowest_vcn))) { + *pnew = *pold; + pnew++; + pold++; + } + /* split a possible old run partially overlapped */ + if (pold->length + && (pold->vcn < (reprl[0].vcn + lowest_vcn))) { + pnew->vcn = pold->vcn; + pnew->lcn = pold->lcn; + pnew->length = reprl[0].vcn + lowest_vcn - pold->vcn; + pnew++; + } + /* copy new runs */ + prep = reprl; + nextvcn = prep->vcn + lowest_vcn; + while (prep->length) { + pnew->vcn = prep->vcn + lowest_vcn; + pnew->lcn = prep->lcn; + pnew->length = prep->length; + nextvcn = pnew->vcn + pnew->length; + pnew++; + prep++; + } + /* locate the first fully replaced old run */ + while (pold->length + && ((pold->vcn + pold->length) <= nextvcn)) { + pold++; + } + /* split a possible old run partially overlapped */ + if (pold->length + && (pold->vcn < nextvcn)) { + pnew->vcn = nextvcn; + pnew->lcn = pold->lcn + nextvcn - pold->vcn; + pnew->length = pold->length - nextvcn + pold->vcn; + pnew++; + } + /* copy old runs beyond replaced ones */ + while (pold->length) { + *pnew = *pold; + pnew++; + pold++; + } + /* the terminator is same as the old one */ + *pnew = *pold; + /* deallocate the old runlist and replace */ + free(na->rl); + na->rl = newrl; + r = 0; + } + return (r); +} + +/* + * Expand the new runlist in new extent(s) + * + * This implies allocating inode extents and, generally, creating + * an attribute list and allocating clusters for the list, and + * shuffle the existing attributes accordingly. + * + * Sometimes the runlist being reallocated is within an extent, + * so we have a partial runlist to plug into an existing one + * whose other parts have already been processed or will have + * to be processed later, and we must not interfere with the + * processing of these parts. + * + * This cannot be done on the runlist part stored in a single + * extent, it has to be done globally for the file. + * + * We use the standard library functions, so we must wait until + * the new global bitmap and the new MFT bitmap are saved to + * disk and usable for the allocation of a new extent and creation + * of an attribute list. + * + * Aborts if something goes wrong. There should be no data damage, + * because the old runlist is still in use and the bootsector has + * not been updated yet, so the initial clusters can be accessed. + */ + +static void expand_attribute_runlist(ntfs_volume *vol, struct DELAYED *delayed) +{ + ntfs_inode *ni; + ntfs_attr *na; + ATTR_TYPES type; + MFT_REF mref; + runlist_element *rl; + + /* open the inode */ + mref = delayed->mref; +#ifndef BAN_NEW_TEXT + ntfs_log_verbose("Processing a delayed update for inode %lld\n", + (long long)mref); +#endif + type = delayed->type; + rl = delayed->rl; + ni = ntfs_inode_open(vol,mref); + if (ni) { + na = ntfs_attr_open(ni, type, + delayed->attr_name, delayed->name_len); + if (na) { + if (!ntfs_attr_map_whole_runlist(na)) { + if (replace_runlist(na,rl,delayed->lowest_vcn) + || ntfs_attr_update_mapping_pairs(na,0)) + perr_exit("Could not update runlist " + "for attribute 0x%lx in inode %lld", + (long)le32_to_cpu(type),(long long)mref); + } else + perr_exit("Could not map attribute 0x%lx in inode %lld", + (long)le32_to_cpu(type),(long long)mref); + ntfs_attr_close(na); + } else + perr_exit("Could not open attribute 0x%lx in inode %lld", + (long)le32_to_cpu(type),(long long)mref); + ntfs_inode_mark_dirty(ni); + if (ntfs_inode_close(ni)) + perr_exit("Failed to close inode %lld through the library", + (long long)mref); + } else + perr_exit("Could not open inode %lld through the library", + (long long)mref); +} + +/* + * Process delayed runlist updates + */ + +static void delayed_updates(ntfs_resize_t *resize) +{ + struct DELAYED *delayed; + + if (ntfs_volume_get_free_space(resize->vol)) + err_exit("Failed to determine free space\n"); + + while (resize->delayed_runlists) { + delayed = resize->delayed_runlists; + expand_attribute_runlist(resize->vol, delayed); + resize->delayed_runlists = resize->delayed_runlists->next; + if (delayed->attr_name) + free(delayed->attr_name); + free(delayed->head_rl); + free(delayed); + } +} + +/* + * Queue a runlist replacement for later update + * + * Store the attribute identification relative to base inode + */ + +static void replace_later(ntfs_resize_t *resize, runlist *rl, runlist *head_rl) +{ + struct DELAYED *delayed; + ATTR_RECORD *a; + MFT_REF mref; + leMFT_REF lemref; + int name_len; + ntfschar *attr_name; + + /* save the attribute parameters, to be able to find it later */ + a = resize->ctx->attr; + name_len = a->name_length; + attr_name = (ntfschar*)NULL; + if (name_len) { + attr_name = (ntfschar*)ntfs_malloc(name_len*sizeof(ntfschar)); + if (attr_name) + memcpy(attr_name,(u8*)a + le16_to_cpu(a->name_offset), + name_len*sizeof(ntfschar)); + } + delayed = (struct DELAYED*)ntfs_malloc(sizeof(struct DELAYED)); + if (delayed && (attr_name || !name_len)) { + lemref = resize->ctx->mrec->base_mft_record; + if (lemref) + mref = le64_to_cpu(lemref); + else + mref = resize->mref; + delayed->mref = MREF(mref); + delayed->type = a->type; + delayed->attr_name = attr_name; + delayed->name_len = name_len; + delayed->lowest_vcn = le64_to_cpu(a->lowest_vcn); + delayed->rl = rl; + delayed->head_rl = head_rl; + delayed->next = resize->delayed_runlists; + resize->delayed_runlists = delayed; + } else + perr_exit("Could not store delayed update data"); +} + +/* + * Replace the runlist in an attribute + * + * This sometimes requires expanding the runlist into another extent, + * which has to be done globally on the attribute. Is so, the action + * is put in a delay queue, and the caller must not free the runlist. + * + * Returns 0 if the replacement could be done + * 1 when it has been put in the delay queue. + */ + +static int replace_attribute_runlist(ntfs_resize_t *resize, runlist *rl) +{ + int mp_size, l; + int must_delay; + void *mp; + runlist *head_rl; + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *a; + + vol = resize->vol; + ctx = resize->ctx; + a = ctx->attr; + head_rl = rl; + rl_fixup(&rl); + + if ((mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, INT_MAX)) == -1) + perr_exit("ntfs_get_size_for_mapping_pairs"); + + if (a->name_length) { + u16 name_offs = le16_to_cpu(a->name_offset); + u16 mp_offs = le16_to_cpu(a->mapping_pairs_offset); + + if (name_offs >= mp_offs) + err_exit("Attribute name is after mapping pairs! " + "Please report!\n"); + } + + /* CHECKME: don't trust mapping_pairs is always the last item in the + attribute, instead check for the real size/space */ + l = (int)le32_to_cpu(a->length) - le16_to_cpu(a->mapping_pairs_offset); + must_delay = 0; + if (mp_size > l) { + s32 remains_size; + char *next_attr; + + ntfs_log_verbose("Enlarging attribute header ...\n"); + + mp_size = (mp_size + 7) & ~7; + + ntfs_log_verbose("Old mp size : %d\n", l); + ntfs_log_verbose("New mp size : %d\n", mp_size); + ntfs_log_verbose("Bytes in use : %u\n", (unsigned int) + le32_to_cpu(ctx->mrec->bytes_in_use)); + + next_attr = (char *)a + le32_to_cpu(a->length); + l = mp_size - l; + + ntfs_log_verbose("Bytes in use new : %u\n", l + (unsigned int) + le32_to_cpu(ctx->mrec->bytes_in_use)); + ntfs_log_verbose("Bytes allocated : %u\n", (unsigned int) + le32_to_cpu(ctx->mrec->bytes_allocated)); + + remains_size = le32_to_cpu(ctx->mrec->bytes_in_use); + remains_size -= (next_attr - (char *)ctx->mrec); + + ntfs_log_verbose("increase : %d\n", l); + ntfs_log_verbose("shift : %lld\n", + (long long)remains_size); + if (le32_to_cpu(ctx->mrec->bytes_in_use) + l > + le32_to_cpu(ctx->mrec->bytes_allocated)) { +#ifndef BAN_NEW_TEXT + ntfs_log_verbose("Queuing expansion for later processing\n"); +#endif + must_delay = 1; + replace_later(resize,rl,head_rl); + } else { + memmove(next_attr + l, next_attr, remains_size); + ctx->mrec->bytes_in_use = cpu_to_le32(l + + le32_to_cpu(ctx->mrec->bytes_in_use)); + a->length = cpu_to_le32(le32_to_cpu(a->length) + l); + } + } + + if (!must_delay) { + mp = ntfs_calloc(mp_size); + if (!mp) + perr_exit("ntfsc_calloc couldn't get memory"); + + if (ntfs_mapping_pairs_build(vol, (u8*)mp, mp_size, rl, 0, NULL)) + perr_exit("ntfs_mapping_pairs_build"); + + memmove((u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp, mp_size); + + free(mp); + } + return (must_delay); +} + +static void set_bitmap_range(struct bitmap *bm, s64 pos, s64 length, u8 bit) +{ + while (length--) + ntfs_bit_set(bm->bm, pos++, bit); +} + +static void set_bitmap_clusters(struct bitmap *bm, runlist *rl, u8 bit) +{ + for (; rl->length; rl++) + set_bitmap_range(bm, rl->lcn, rl->length, bit); +} + +static void release_bitmap_clusters(struct bitmap *bm, runlist *rl) +{ + max_free_cluster_range = 0; + set_bitmap_clusters(bm, rl, 0); +} + +static void set_max_free_zone(s64 length, s64 end, runlist_element *rle) +{ + if (length > rle->length) { + rle->lcn = end - length; + rle->length = length; + } +} + +static int find_free_cluster(struct bitmap *bm, + runlist_element *rle, + s64 nr_vol_clusters, + int hint) +{ + /* FIXME: get rid of this 'static' variable */ + static s64 pos = 0; + s64 i, items = rle->length; + s64 free_zone = 0; + + if (pos >= nr_vol_clusters) + pos = 0; + if (!max_free_cluster_range) + max_free_cluster_range = nr_vol_clusters; + rle->lcn = rle->length = 0; + if (hint) + pos = nr_vol_clusters / 2; + i = pos; + + do { + if (!ntfs_bit_get(bm->bm, i)) { + if (++free_zone == items) { + set_max_free_zone(free_zone, i + 1, rle); + break; + } + } else { + set_max_free_zone(free_zone, i, rle); + free_zone = 0; + } + if (++i == nr_vol_clusters) { + set_max_free_zone(free_zone, i, rle); + i = free_zone = 0; + } + if (rle->length == max_free_cluster_range) + break; + } while (i != pos); + + if (i) + set_max_free_zone(free_zone, i, rle); + + if (!rle->lcn) { + errno = ENOSPC; + return -1; + } + if (rle->length < items && rle->length < max_free_cluster_range) { + max_free_cluster_range = rle->length; + ntfs_log_verbose("Max free range: %7lld \n", + (long long)max_free_cluster_range); + } + pos = rle->lcn + items; + if (pos == nr_vol_clusters) + pos = 0; + + set_bitmap_range(bm, rle->lcn, rle->length, 1); + return 0; +} + +static runlist *alloc_cluster(struct bitmap *bm, + s64 items, + s64 nr_vol_clusters, + int hint) +{ + runlist_element rle; + runlist *rl = NULL; + int rl_size, runs = 0; + s64 vcn = 0; + + if (items <= 0) { + errno = EINVAL; + return NULL; + } + + while (items > 0) { + + if (runs) + hint = 0; + rle.length = items; + if (find_free_cluster(bm, &rle, nr_vol_clusters, hint) == -1) + return NULL; + + rl_size = (runs + 2) * sizeof(runlist_element); + if (!(rl = (runlist *)realloc(rl, rl_size))) + return NULL; + + rl_set(rl + runs, vcn, rle.lcn, rle.length); + + vcn += rle.length; + items -= rle.length; + runs++; + } + + rl_set(rl + runs, vcn, -1LL, 0LL); + + if (runs > 1) { + ntfs_log_verbose("Multi-run allocation: \n"); + dump_runlist(rl); + } + return rl; +} + +static int read_all(struct ntfs_device *dev, void *buf, int count) +{ + int i; + + while (count > 0) { + + i = count; + if (!NDevReadOnly(dev)) + i = dev->d_ops->read(dev, buf, count); + + if (i < 0) { + if (errno != EAGAIN && errno != EINTR) + return -1; + } else if (i > 0) { + count -= i; + buf = i + (char *)buf; + } else + err_exit("Unexpected end of file!\n"); + } + return 0; +} + +static int write_all(struct ntfs_device *dev, void *buf, int count) +{ + int i; + + while (count > 0) { + + i = count; + if (!NDevReadOnly(dev)) + i = dev->d_ops->write(dev, buf, count); + + if (i < 0) { + if (errno != EAGAIN && errno != EINTR) + return -1; + } else { + count -= i; + buf = i + (char *)buf; + } + } + return 0; +} + +/** + * write_mft_record + * + * Write an MFT Record back to the disk. If the read-only command line option + * was given, this function will do nothing. + */ +static int write_mft_record(ntfs_volume *v, const MFT_REF mref, MFT_RECORD *buf) +{ + if (ntfs_mft_record_write(v, mref, buf)) + perr_exit("ntfs_mft_record_write"); + +// if (v->dev->d_ops->sync(v->dev) == -1) +// perr_exit("Failed to sync device"); + + return 0; +} + +static void lseek_to_cluster(ntfs_volume *vol, s64 lcn) +{ + off_t pos; + + pos = (off_t)(lcn * vol->cluster_size); + + if (vol->dev->d_ops->seek(vol->dev, pos, SEEK_SET) == (off_t)-1) + perr_exit("seek failed to position %lld", (long long)lcn); +} + +static void copy_clusters(ntfs_resize_t *resize, s64 dest, s64 src, s64 len) +{ + s64 i; + char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ + ntfs_volume *vol = resize->vol; + + for (i = 0; i < len; i++) { + + lseek_to_cluster(vol, src + i); + + if (read_all(vol->dev, buff, vol->cluster_size) == -1) { + perr_printf("Failed to read from the disk"); + if (errno == EIO) + printf("%s", bad_sectors_warning_msg); + exit(1); + } + + lseek_to_cluster(vol, dest + i); + + if (write_all(vol->dev, buff, vol->cluster_size) == -1) { + perr_printf("Failed to write to the disk"); + if (errno == EIO) + printf("%s", bad_sectors_warning_msg); + exit(1); + } + + resize->relocations++; + progress_update(&resize->progress, resize->relocations); + } +} + +static void relocate_clusters(ntfs_resize_t *r, runlist *dest_rl, s64 src_lcn) +{ + /* collect_shrink_constraints() ensured $MFTMir DATA is one run */ + if (r->mref == FILE_MFTMirr && r->ctx->attr->type == AT_DATA) { + if (!r->mftmir_old) { + r->mftmir_rl.lcn = dest_rl->lcn; + r->mftmir_rl.length = dest_rl->length; + r->mftmir_old = src_lcn; + } else + err_exit("Multi-run $MFTMirr. Please report!\n"); + } + + for (; dest_rl->length; src_lcn += dest_rl->length, dest_rl++) + copy_clusters(r, dest_rl->lcn, src_lcn, dest_rl->length); +} + +static void rl_split_run(runlist **rl, int run, s64 pos) +{ + runlist *rl_new, *rle_new, *rle; + int items, new_size, size_head, size_tail; + s64 len_head, len_tail; + + items = rl_items(*rl); + new_size = (items + 1) * sizeof(runlist_element); + size_head = run * sizeof(runlist_element); + size_tail = (items - run - 1) * sizeof(runlist_element); + + rl_new = ntfs_malloc(new_size); + if (!rl_new) + perr_exit("ntfs_malloc"); + + rle_new = rl_new + run; + rle = *rl + run; + + memmove(rl_new, *rl, size_head); + memmove(rle_new + 2, rle + 1, size_tail); + + len_tail = rle->length - (pos - rle->lcn); + len_head = rle->length - len_tail; + + rl_set(rle_new, rle->vcn, rle->lcn, len_head); + rl_set(rle_new + 1, rle->vcn + len_head, rle->lcn + len_head, len_tail); + + ntfs_log_verbose("Splitting run at cluster %lld:\n", (long long)pos); + dump_run(rle); dump_run(rle_new); dump_run(rle_new + 1); + + free(*rl); + *rl = rl_new; +} + +static void rl_insert_at_run(runlist **rl, int run, runlist *ins) +{ + int items, ins_items; + int new_size, size_tail; + runlist *rle; + s64 vcn; + + items = rl_items(*rl); + ins_items = rl_items(ins) - 1; + new_size = ((items - 1) + ins_items) * sizeof(runlist_element); + size_tail = (items - run - 1) * sizeof(runlist_element); + + if (!(*rl = (runlist *)realloc(*rl, new_size))) + perr_exit("realloc"); + + rle = *rl + run; + + memmove(rle + ins_items, rle + 1, size_tail); + + for (vcn = rle->vcn; ins->length; rle++, vcn += ins->length, ins++) { + rl_set(rle, vcn, ins->lcn, ins->length); +// dump_run(rle); + } + + return; + + /* FIXME: fast path if ins_items = 1 */ +// (*rl + run)->lcn = ins->lcn; +} + +static void relocate_run(ntfs_resize_t *resize, runlist **rl, int run) +{ + s64 lcn, lcn_length; + s64 new_vol_size; /* (last LCN on the volume) + 1 */ + runlist *relocate_rl; /* relocate runlist to relocate_rl */ + int hint; + + lcn = (*rl + run)->lcn; + lcn_length = (*rl + run)->length; + new_vol_size = resize->new_volume_size; + + if (lcn + lcn_length <= new_vol_size) + return; + + if (lcn < new_vol_size) { + rl_split_run(rl, run, new_vol_size); + return; + } + + hint = (resize->mref == FILE_MFTMirr) ? 1 : 0; + if ((resize->mref == FILE_MFT) + && (resize->ctx->attr->type == AT_DATA) + && !run + && resize->new_mft_start) { + relocate_rl = resize->new_mft_start; + } else + if (!(relocate_rl = alloc_cluster(&resize->lcn_bitmap, + lcn_length, new_vol_size, hint))) + perr_exit("Cluster allocation failed for %llu:%lld", + (unsigned long long)resize->mref, + (long long)lcn_length); + + /* FIXME: check $MFTMirr DATA isn't multi-run (or support it) */ + ntfs_log_verbose("Relocate record %7llu:0x%x:%08lld:0x%08llx:0x%08llx " + "--> 0x%08llx\n", (unsigned long long)resize->mref, + (unsigned int)le32_to_cpu(resize->ctx->attr->type), + (long long)lcn_length, + (unsigned long long)(*rl + run)->vcn, + (unsigned long long)lcn, + (unsigned long long)relocate_rl->lcn); + + relocate_clusters(resize, relocate_rl, lcn); + rl_insert_at_run(rl, run, relocate_rl); + + /* We don't release old clusters in the bitmap, that area isn't + used by the allocator and will be truncated later on */ + + /* Do not free the relocated MFT start */ + if ((resize->mref != FILE_MFT) + || (resize->ctx->attr->type != AT_DATA) + || run + || !resize->new_mft_start) + free(relocate_rl); + + resize->dirty_inode = DIRTY_ATTRIB; +} + +static void relocate_attribute(ntfs_resize_t *resize) +{ + ATTR_RECORD *a; + runlist *rl; + int i; + + a = resize->ctx->attr; + + if (!a->non_resident) + return; + + if (!(rl = ntfs_mapping_pairs_decompress(resize->vol, a, NULL))) + perr_exit("ntfs_decompress_mapping_pairs"); + + for (i = 0; rl[i].length; i++) { + s64 lcn = rl[i].lcn; + s64 lcn_length = rl[i].length; + + if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) + continue; + + /* FIXME: ntfs_mapping_pairs_decompress should return error */ + if (lcn < 0 || lcn_length <= 0) + err_exit("Corrupt runlist in MTF %llu attr %x LCN " + "%llx length %llx\n", + (unsigned long long)resize->mref, + (unsigned int)le32_to_cpu(a->type), + (long long)lcn, (long long)lcn_length); + + relocate_run(resize, &rl, i); + } + + if (resize->dirty_inode == DIRTY_ATTRIB) { + if (!replace_attribute_runlist(resize, rl)) + free(rl); + resize->dirty_inode = DIRTY_INODE; + } else + free(rl); +} + +static int is_mftdata(ntfs_resize_t *resize) +{ + /* + * We must update the MFT own DATA record at the end of the second + * step, because the old MFT must be kept available for processing + * the other files. + */ + + if (resize->ctx->attr->type != AT_DATA) + return 0; + + if (resize->mref == 0) + return 1; + + if (MREF_LE(resize->mrec->base_mft_record) == 0 && + MSEQNO_LE(resize->mrec->base_mft_record) != 0) + return 1; + + return 0; +} + +static int handle_mftdata(ntfs_resize_t *resize, int do_mftdata) +{ + ATTR_RECORD *attr = resize->ctx->attr; + VCN highest_vcn, lowest_vcn; + + if (do_mftdata) { + + if (!is_mftdata(resize)) + return 0; + + highest_vcn = sle64_to_cpu(attr->highest_vcn); + lowest_vcn = sle64_to_cpu(attr->lowest_vcn); + + if (resize->mft_highest_vcn != highest_vcn) + return 0; + + if (lowest_vcn == 0) + resize->mft_highest_vcn = lowest_vcn; + else + resize->mft_highest_vcn = lowest_vcn - 1; + + } else if (is_mftdata(resize)) { + + highest_vcn = sle64_to_cpu(attr->highest_vcn); + + if (resize->mft_highest_vcn < highest_vcn) + resize->mft_highest_vcn = highest_vcn; + + return 0; + } + + return 1; +} + +static void relocate_attributes(ntfs_resize_t *resize, int do_mftdata) +{ + int ret; + + if (!(resize->ctx = attr_get_search_ctx(NULL, resize->mrec))) + exit(1); + + while (!ntfs_attrs_walk(resize->ctx)) { + if (resize->ctx->attr->type == AT_END) + break; + + if (handle_mftdata(resize, do_mftdata) == 0) + continue; + + ret = ntfs_inode_badclus_bad(resize->mref, resize->ctx->attr); + if (ret == -1) + perr_exit("Bad sector list check failed"); + else if (ret == 1) + continue; + + if (resize->mref == FILE_Bitmap && + resize->ctx->attr->type == AT_DATA) + continue; + + relocate_attribute(resize); + } + + ntfs_attr_put_search_ctx(resize->ctx); +} + +static void relocate_inode(ntfs_resize_t *resize, MFT_REF mref, int do_mftdata) +{ + ntfs_volume *vol = resize->vol; + + if (ntfs_file_record_read(vol, mref, &resize->mrec, NULL)) { + /* FIXME: continue only if it make sense, e.g. + MFT record not in use based on $MFT bitmap */ + if (errno == EIO || errno == ENOENT) + return; + perr_exit("ntfs_file_record_record"); + } + + if (!(resize->mrec->flags & MFT_RECORD_IN_USE)) + return; + + resize->mref = mref; + resize->dirty_inode = DIRTY_NONE; + + relocate_attributes(resize, do_mftdata); + +// if (vol->dev->d_ops->sync(vol->dev) == -1) +// perr_exit("Failed to sync device"); + /* relocate MFT during second step, even if not dirty */ + if ((mref == FILE_MFT) && do_mftdata && resize->new_mft_start) { + s64 pos; + + /* write the MFT own record at its new location */ + pos = (resize->new_mft_start->lcn + << vol->cluster_size_bits) + + (FILE_MFT << vol->mft_record_size_bits); + if (!opt.ro_flag + && (ntfs_mst_pwrite(vol->dev, pos, 1, + vol->mft_record_size, resize->mrec) != 1)) + perr_exit("Couldn't update MFT own record"); + } else { + if ((resize->dirty_inode == DIRTY_INODE) + && write_mft_record(vol, mref, resize->mrec)) { + perr_exit("Couldn't update record %llu", + (unsigned long long)mref); + } + } +} + +static void relocate_inodes(ntfs_resize_t *resize) +{ + s64 nr_mft_records; + MFT_REF mref; + VCN highest_vcn; + u64 length; + + printf("Relocating needed data ...\n"); + + progress_init(&resize->progress, 0, resize->relocations, resize->progress.flags); + resize->relocations = 0; + + resize->mrec = ntfs_malloc(resize->vol->mft_record_size); + if (!resize->mrec) + perr_exit("ntfs_malloc failed"); + + nr_mft_records = resize->vol->mft_na->initialized_size >> + resize->vol->mft_record_size_bits; + + /* + * If we need to relocate the first run of the MFT DATA, + * do it now, to have a better chance of getting at least + * 16 records in the first chunk. This is mandatory to be + * later able to read an MFT extent in record 15. + * Should this fail, we can stop with no damage, the volume + * is still in its initial state. + */ + if (!resize->vol->mft_na->rl) + err_exit("Internal error : no runlist for $MFT\n"); + + if ((resize->vol->mft_na->rl->lcn + resize->vol->mft_na->rl->length) + >= resize->new_volume_size) { + /* + * The length of the first run is normally found in + * mft_na. However in some rare circumstance, this is + * merged with the first run of an extent of MFT, + * which implies there is a single run in the base record. + * So we have to make sure not to overflow from the + * runs present in the base extent. + */ + length = resize->vol->mft_na->rl->length; + if (ntfs_file_record_read(resize->vol, FILE_MFT, + &resize->mrec, NULL) + || !(resize->ctx = attr_get_search_ctx(NULL, + resize->mrec))) { + err_exit("Could not read the base record of MFT\n"); + } + while (!ntfs_attrs_walk(resize->ctx) + && (resize->ctx->attr->type != AT_DATA)) { } + if (resize->ctx->attr->type == AT_DATA) { + le64 high_le; + + high_le = resize->ctx->attr->highest_vcn; + if (le64_to_cpu(high_le) < length) + length = le64_to_cpu(high_le) + 1; + } else { + err_exit("Could not find the DATA of MFT\n"); + } + ntfs_attr_put_search_ctx(resize->ctx); + resize->new_mft_start = alloc_cluster(&resize->lcn_bitmap, + length, resize->new_volume_size, 0); + if (!resize->new_mft_start + || (((resize->new_mft_start->length + << resize->vol->cluster_size_bits) + >> resize->vol->mft_record_size_bits) < 16)) { + err_exit("Could not allocate 16 records in" + " the first MFT chunk\n"); + } + } + + for (mref = 0; mref < (MFT_REF)nr_mft_records; mref++) + relocate_inode(resize, mref, 0); + + while (1) { + highest_vcn = resize->mft_highest_vcn; + mref = nr_mft_records; + do { + relocate_inode(resize, --mref, 1); + if (resize->mft_highest_vcn == 0) + goto done; + } while (mref); + + if (highest_vcn == resize->mft_highest_vcn) + err_exit("Sanity check failed! Highest_vcn = %lld. " + "Please report!\n", (long long)highest_vcn); + } +done: + free(resize->mrec); +} + +static void print_hint(ntfs_volume *vol, const char *s, struct llcn_t llcn) +{ + s64 runs_b, runs_mb; + + if (llcn.lcn == 0) + return; + + runs_b = llcn.lcn * vol->cluster_size; + runs_mb = rounded_up_division(runs_b, NTFS_MBYTE); + printf("%-19s: %9lld MB %8lld\n", s, (long long)runs_mb, + (long long)llcn.inode); +} + +/** + * advise_on_resize + * + * The metadata file $Bitmap has one bit for each cluster on disk. This has + * already been read into lcn_bitmap. By looking for the last used cluster on + * the disk, we can work out by how much we can shrink the volume. + */ +static void advise_on_resize(ntfs_resize_t *resize) +{ + ntfs_volume *vol = resize->vol; + + if (opt.verbose) { + printf("Estimating smallest shrunken size supported ...\n"); + printf("File feature Last used at By inode\n"); + print_hint(vol, "$MFT", resize->last_mft); + print_hint(vol, "Multi-Record", resize->last_multi_mft); + print_hint(vol, "$MFTMirr", resize->last_mftmir); + print_hint(vol, "Compressed", resize->last_compressed); + print_hint(vol, "Sparse", resize->last_sparse); + print_hint(vol, "Ordinary", resize->last_lcn); + } + + print_advise(vol, resize->last_unsupp); +} + +static void rl_expand(runlist **rl, const VCN last_vcn) +{ + int len; + runlist *p = *rl; + + len = rl_items(p) - 1; + if (len <= 0) + err_exit("rl_expand: bad runlist length: %d\n", len); + + if (p[len].vcn > last_vcn) + err_exit("rl_expand: length is already more than requested " + "(%lld > %lld)\n", + (long long)p[len].vcn, (long long)last_vcn); + + if (p[len - 1].lcn == LCN_HOLE) { + + p[len - 1].length += last_vcn - p[len].vcn; + p[len].vcn = last_vcn; + + } else if (p[len - 1].lcn >= 0) { + + p = realloc(*rl, (++len + 1) * sizeof(runlist_element)); + if (!p) + perr_exit("rl_expand: realloc"); + + p[len - 1].lcn = LCN_HOLE; + p[len - 1].length = last_vcn - p[len - 1].vcn; + rl_set(p + len, last_vcn, LCN_ENOENT, 0LL); + *rl = p; + + } else + err_exit("rl_expand: bad LCN: %lld\n", + (long long)p[len - 1].lcn); +} + +static void rl_truncate(runlist **rl, const VCN last_vcn) +{ + int len; + VCN vcn; + + len = rl_items(*rl) - 1; + if (len <= 0) + err_exit("rl_truncate: bad runlist length: %d\n", len); + + vcn = (*rl)[len].vcn; + + if (vcn < last_vcn) + rl_expand(rl, last_vcn); + + else if (vcn > last_vcn) + if (ntfs_rl_truncate(rl, last_vcn) == -1) + perr_exit("ntfs_rl_truncate"); +} + +/** + * bitmap_file_data_fixup + * + * $Bitmap can overlap the end of the volume. Any bits in this region + * must be set. This region also encompasses the backup boot sector. + */ +static void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm) +{ + for (; cluster < bm->size << 3; cluster++) + ntfs_bit_set(bm->bm, (u64)cluster, 1); +} + +/** + * truncate_badclust_bad_attr + * + * The metadata file $BadClus needs to be shrunk. + * + * FIXME: this function should go away and instead using a generalized + * "truncate_bitmap_data_attr()" + */ +static void truncate_badclust_bad_attr(ntfs_resize_t *resize) +{ + ATTR_RECORD *a; + runlist *rl_bad; + s64 nr_clusters = resize->new_volume_size; + ntfs_volume *vol = resize->vol; + + a = resize->ctx->attr; + if (!a->non_resident) + /* FIXME: handle resident attribute value */ + err_exit("Resident attribute in $BadClust isn't supported!\n"); + + if (!(rl_bad = ntfs_mapping_pairs_decompress(vol, a, NULL))) + perr_exit("ntfs_mapping_pairs_decompress"); + + rl_truncate(&rl_bad, nr_clusters); + + a->highest_vcn = cpu_to_sle64(nr_clusters - 1LL); + a->allocated_size = cpu_to_sle64(nr_clusters * vol->cluster_size); + a->data_size = cpu_to_sle64(nr_clusters * vol->cluster_size); + + if (!replace_attribute_runlist(resize, rl_bad)) + free(rl_bad); +} + +/** + * realloc_bitmap_data_attr + * + * Reallocate the metadata file $Bitmap. It must be large enough for one bit + * per cluster of the shrunken volume. Also it must be a of 8 bytes in size. + */ +static void realloc_bitmap_data_attr(ntfs_resize_t *resize, + runlist **rl, + s64 nr_bm_clusters) +{ + s64 i; + ntfs_volume *vol = resize->vol; + ATTR_RECORD *a = resize->ctx->attr; + s64 new_size = resize->new_volume_size; + struct bitmap *bm = &resize->lcn_bitmap; + + if (!(*rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) + perr_exit("ntfs_mapping_pairs_decompress"); + + release_bitmap_clusters(bm, *rl); + free(*rl); + + for (i = vol->nr_clusters; i < new_size; i++) + ntfs_bit_set(bm->bm, i, 0); + + if (!(*rl = alloc_cluster(bm, nr_bm_clusters, new_size, 0))) + perr_exit("Couldn't allocate $Bitmap clusters"); +} + +static void realloc_lcn_bitmap(ntfs_resize_t *resize, s64 bm_bsize) +{ + u8 *tmp; + + if (!(tmp = realloc(resize->lcn_bitmap.bm, bm_bsize))) + perr_exit("realloc"); + + resize->lcn_bitmap.bm = tmp; + resize->lcn_bitmap.size = bm_bsize; + bitmap_file_data_fixup(resize->new_volume_size, &resize->lcn_bitmap); +} + +/** + * truncate_bitmap_data_attr + */ +static void truncate_bitmap_data_attr(ntfs_resize_t *resize) +{ + ATTR_RECORD *a; + runlist *rl; + ntfs_attr *lcnbmp_na; + s64 bm_bsize, size; + s64 nr_bm_clusters; + int truncated; + ntfs_volume *vol = resize->vol; + + a = resize->ctx->attr; + if (!a->non_resident) + /* FIXME: handle resident attribute value */ + err_exit("Resident attribute in $Bitmap isn't supported!\n"); + + bm_bsize = nr_clusters_to_bitmap_byte_size(resize->new_volume_size); + nr_bm_clusters = rounded_up_division(bm_bsize, vol->cluster_size); + + if (resize->shrink) { + realloc_bitmap_data_attr(resize, &rl, nr_bm_clusters); + realloc_lcn_bitmap(resize, bm_bsize); + } else { + realloc_lcn_bitmap(resize, bm_bsize); + realloc_bitmap_data_attr(resize, &rl, nr_bm_clusters); + } + /* + * Delayed relocations may require cluster allocations + * through the library, to hold added attribute lists, + * be sure they will be within the new limits. + */ + lcnbmp_na = resize->vol->lcnbmp_na; + lcnbmp_na->data_size = bm_bsize; + lcnbmp_na->initialized_size = bm_bsize; + lcnbmp_na->allocated_size = nr_bm_clusters << vol->cluster_size_bits; + vol->lcnbmp_ni->data_size = bm_bsize; + vol->lcnbmp_ni->allocated_size = lcnbmp_na->allocated_size; + a->highest_vcn = cpu_to_sle64(nr_bm_clusters - 1LL); + a->allocated_size = cpu_to_sle64(nr_bm_clusters * vol->cluster_size); + a->data_size = cpu_to_sle64(bm_bsize); + a->initialized_size = cpu_to_sle64(bm_bsize); + + truncated = !replace_attribute_runlist(resize, rl); + + /* + * FIXME: update allocated/data sizes and timestamps in $FILE_NAME + * attribute too, for now chkdsk will do this for us. + */ + + size = ntfs_rl_pwrite(vol, rl, 0, 0, bm_bsize, resize->lcn_bitmap.bm); + if (bm_bsize != size) { + if (size == -1) + perr_exit("Couldn't write $Bitmap"); + err_exit("Couldn't write full $Bitmap file (%lld from %lld)\n", + (long long)size, (long long)bm_bsize); + } + + if (truncated) { + /* switch to the new bitmap runlist */ + free(lcnbmp_na->rl); + lcnbmp_na->rl = rl; + } +} + +/** + * lookup_data_attr + * + * Find the $DATA attribute (with or without a name) for the given MFT reference + * (inode number). + */ +static void lookup_data_attr(ntfs_volume *vol, + MFT_REF mref, + const char *aname, + ntfs_attr_search_ctx **ctx) +{ + ntfs_inode *ni; + ntfschar *ustr; + int len = 0; + + if (!(ni = ntfs_inode_open(vol, mref))) + perr_exit("ntfs_open_inode"); + + if (!(*ctx = attr_get_search_ctx(ni, NULL))) + exit(1); + + if ((ustr = ntfs_str2ucs(aname, &len)) == NULL) { + perr_printf("Couldn't convert '%s' to Unicode", aname); + exit(1); + } + + if (ntfs_attr_lookup(AT_DATA, ustr, len, CASE_SENSITIVE, + 0, NULL, 0, *ctx)) + perr_exit("ntfs_lookup_attr"); + + ntfs_ucsfree(ustr); +} + +#if CLEAN_EXIT + +static void close_inode_and_context(ntfs_attr_search_ctx *ctx) +{ + ntfs_inode *ni; + + ni = ctx->base_ntfs_ino; + if (!ni) + ni = ctx->ntfs_ino; + ntfs_attr_put_search_ctx(ctx); + if (ni) + ntfs_inode_close(ni); +} + +#endif /* CLEAN_EXIT */ + +static int check_bad_sectors(ntfs_volume *vol) +{ + ntfs_attr_search_ctx *ctx; + ntfs_inode *base_ni; + runlist *rl; + s64 i, badclusters = 0; + + ntfs_log_verbose("Checking for bad sectors ...\n"); + + lookup_data_attr(vol, FILE_BadClus, "$Bad", &ctx); + + base_ni = ctx->base_ntfs_ino; + if (!base_ni) + base_ni = ctx->ntfs_ino; + + if (NInoAttrList(base_ni)) { + err_printf("Too many bad sectors have been detected!\n"); + printf("%s", many_bad_sectors_msg); + exit(1); + } + + if (!ctx->attr->non_resident) + err_exit("Resident attribute in $BadClust! Please report to " + "%s\n", NTFS_DEV_LIST); + /* + * FIXME: The below would be partial for non-base records in the + * not yet supported multi-record case. Alternatively use audited + * ntfs_attr_truncate after an umount & mount. + */ + if (!(rl = ntfs_mapping_pairs_decompress(vol, ctx->attr, NULL))) + perr_exit("Decompressing $BadClust:$Bad mapping pairs failed"); + + for (i = 0; rl[i].length; i++) { + /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ + if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED) + continue; + + badclusters += rl[i].length; + ntfs_log_verbose("Bad cluster: %#8llx - %#llx (%lld)\n", + (long long)rl[i].lcn, + (long long)rl[i].lcn + rl[i].length - 1, + (long long)rl[i].length); + } + + if (badclusters) { + printf("%sThis software has detected that the disk has at least" + " %lld bad sector%s.\n", + !opt.badsectors ? NERR_PREFIX : "WARNING: ", + (long long)badclusters, badclusters - 1 ? "s" : ""); + if (!opt.badsectors) { + printf("%s", bad_sectors_warning_msg); + exit(1); + } else + printf("WARNING: Bad sectors can cause reliability " + "problems and massive data loss!!!\n"); + } + + free(rl); +#if CLEAN_EXIT + close_inode_and_context(ctx); +#else + ntfs_attr_put_search_ctx(ctx); +#endif + + return badclusters; +} + +/** + * truncate_badclust_file + * + * Shrink the $BadClus file to match the new volume size. + */ +static void truncate_badclust_file(ntfs_resize_t *resize) +{ + printf("Updating $BadClust file ...\n"); + + lookup_data_attr(resize->vol, FILE_BadClus, "$Bad", &resize->ctx); + /* FIXME: sanity_check_attr(ctx->attr); */ + truncate_badclust_bad_attr(resize); + + if (write_mft_record(resize->vol, resize->ctx->ntfs_ino->mft_no, + resize->ctx->mrec)) + perr_exit("Couldn't update $BadClust"); + +#if CLEAN_EXIT + close_inode_and_context(resize->ctx); +#else + ntfs_attr_put_search_ctx(resize->ctx); +#endif +} + +/** + * truncate_bitmap_file + * + * Shrink the $Bitmap file to match the new volume size. + */ +static void truncate_bitmap_file(ntfs_resize_t *resize) +{ + ntfs_volume *vol = resize->vol; + + printf("Updating $Bitmap file ...\n"); + + lookup_data_attr(resize->vol, FILE_Bitmap, NULL, &resize->ctx); + truncate_bitmap_data_attr(resize); + + if (resize->new_mft_start) { + s64 pos; + + /* write the MFT record at its new location */ + pos = (resize->new_mft_start->lcn << vol->cluster_size_bits) + + (FILE_Bitmap << vol->mft_record_size_bits); + if (!opt.ro_flag + && (ntfs_mst_pwrite(vol->dev, pos, 1, + vol->mft_record_size, resize->ctx->mrec) != 1)) + perr_exit("Couldn't update $Bitmap at new location"); + } else { + if (write_mft_record(vol, resize->ctx->ntfs_ino->mft_no, + resize->ctx->mrec)) + perr_exit("Couldn't update $Bitmap"); + } + + /* If successful, update cache and sync $Bitmap */ + memcpy(vol->lcnbmp_ni->mrec,resize->ctx->mrec,vol->mft_record_size); + ntfs_inode_mark_dirty(vol->lcnbmp_ni); + NInoFileNameSetDirty(vol->lcnbmp_ni); + ntfs_inode_sync(vol->lcnbmp_ni); + +#if CLEAN_EXIT + close_inode_and_context(resize->ctx); +#else + ntfs_attr_put_search_ctx(resize->ctx); +#endif +} + +/** + * setup_lcn_bitmap + * + * Allocate a block of memory with one bit for each cluster of the disk. + * All the bits are set to 0, except those representing the region beyond the + * end of the disk. + */ +static int setup_lcn_bitmap(struct bitmap *bm, s64 nr_clusters) +{ + /* Determine lcn bitmap byte size and allocate it. */ + bm->size = rounded_up_division(nr_clusters, 8); + + bm->bm = ntfs_calloc(bm->size); + if (!bm->bm) + return -1; + + bitmap_file_data_fixup(nr_clusters, bm); + return 0; +} + +/** + * update_bootsector + * + * FIXME: should be done using ntfs_* functions + */ +static void update_bootsector(ntfs_resize_t *r) +{ + NTFS_BOOT_SECTOR *bs; + ntfs_volume *vol = r->vol; + s64 bs_size = vol->sector_size; + + printf("Updating Boot record ...\n"); + + bs = (NTFS_BOOT_SECTOR*)ntfs_malloc(vol->sector_size); + if (!bs) + perr_exit("ntfs_malloc"); + + if (vol->dev->d_ops->seek(vol->dev, 0, SEEK_SET) == (off_t)-1) + perr_exit("lseek"); + + if (vol->dev->d_ops->read(vol->dev, bs, bs_size) == -1) + perr_exit("read() error"); + + bs->number_of_sectors = cpu_to_sle64(r->new_volume_size * + bs->bpb.sectors_per_cluster); + + if (r->mftmir_old) { + r->progress.flags |= NTFS_PROGBAR_SUPPRESS; + /* Be sure the MFTMirr holds the updated MFT runlist */ + if (r->new_mft_start) + copy_clusters(r, r->mftmir_rl.lcn, + r->new_mft_start->lcn, r->mftmir_rl.length); + else + copy_clusters(r, r->mftmir_rl.lcn, r->mftmir_old, + r->mftmir_rl.length); + bs->mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn); + r->progress.flags &= ~NTFS_PROGBAR_SUPPRESS; + } + /* Set the start of the relocated MFT */ + if (r->new_mft_start) { + bs->mft_lcn = cpu_to_sle64(r->new_mft_start->lcn); + /* no more need for the new MFT start */ + free(r->new_mft_start); + r->new_mft_start = (runlist_element*)NULL; + } + + if (vol->dev->d_ops->seek(vol->dev, 0, SEEK_SET) == (off_t)-1) + perr_exit("lseek"); + + if (!opt.ro_flag) + if (vol->dev->d_ops->write(vol->dev, bs, bs_size) == -1) + perr_exit("write() error"); + /* + * Set the backup boot sector, if the target size is + * either not defined or is defined with no multiplier + * suffix and is a multiple of the sector size. + * With these conditions we can be confident enough that + * the partition size is already defined or it will be + * later defined with the same exact value. + */ + if (!opt.ro_flag && opt.reliable_size + && !(opt.bytes % vol->sector_size)) { + if (vol->dev->d_ops->seek(vol->dev, opt.bytes + - vol->sector_size, SEEK_SET) == (off_t)-1) + perr_exit("lseek"); + if (vol->dev->d_ops->write(vol->dev, bs, bs_size) == -1) + perr_exit("write() error"); + } + free(bs); +} + +/** + * vol_size + */ +static s64 vol_size(ntfs_volume *v, s64 nr_clusters) +{ + /* add one sector_size for the backup boot sector */ + return nr_clusters * v->cluster_size + v->sector_size; +} + +/** + * print_vol_size + * + * Print the volume size in bytes and decimal megabytes. + */ +static void print_vol_size(const char *str, s64 bytes) +{ + printf("%s: %lld bytes (%lld MB)\n", str, (long long)bytes, + (long long)rounded_up_division(bytes, NTFS_MBYTE)); +} + +/** + * print_disk_usage + * + * Display the amount of disk space in use. + */ +static void print_disk_usage(ntfs_volume *vol, s64 nr_used_clusters) +{ + s64 total, used; + + total = vol->nr_clusters * vol->cluster_size; + used = nr_used_clusters * vol->cluster_size; + + /* WARNING: don't modify the text, external tools grep for it */ + if (!opt.infombonly) { + printf("Space in use : %lld MB (%.1f%%)\n", + (long long)rounded_up_division(used, NTFS_MBYTE), + 100.0 * ((float)used / total)); + } +} + +static void print_num_of_relocations(ntfs_resize_t *resize) +{ + s64 relocations = resize->relocations * resize->vol->cluster_size; + + printf("Needed relocations : %lld (%lld MB)\n", + (long long)resize->relocations, (long long) + rounded_up_division(relocations, NTFS_MBYTE)); +} + +static ntfs_volume *check_volume(void) +{ + ntfs_volume *myvol = NULL; + + /* + * Pass NTFS_MNT_FORENSIC so that the mount process does not modify the + * volume at all. We will do the logfile emptying and dirty setting + * later if needed. + */ + if (!(myvol = ntfs_mount(opt.volume, opt.ro_flag | NTFS_MNT_FORENSIC))) + { + int err = errno; + + perr_printf("Opening '%s' as NTFS failed", opt.volume); + switch (err) { + case EINVAL : + printf(invalid_ntfs_msg, opt.volume); + break; + case EIO : + printf("%s", corrupt_volume_msg); + break; + case EPERM : + printf("%s", hibernated_volume_msg); + break; + case EOPNOTSUPP : + printf("%s", unclean_journal_msg); + break; + case EBUSY : + printf("%s", opened_volume_msg); + break; + default : + break; + } + exit(1); + } + return myvol; +} + + +/** + * mount_volume + * + * First perform some checks to determine if the volume is already mounted, or + * is dirty (Windows wasn't shutdown properly). If everything is OK, then mount + * the volume (load the metadata into memory). + */ +static ntfs_volume *mount_volume(void) +{ + unsigned long mntflag; + ntfs_volume *vol = NULL; + + if (ntfs_check_if_mounted(opt.volume, &mntflag)) { + perr_printf("Failed to check '%s' mount state", opt.volume); + printf("Probably /etc/mtab is missing. It's too risky to " + "continue. You might try\nan another Linux distro.\n"); + exit(1); + } + if (mntflag & NTFS_MF_MOUNTED) { + if (!(mntflag & NTFS_MF_READONLY)) + err_exit("Device '%s' is mounted read-write. " + "You must 'umount' it first.\n", opt.volume); + if (!opt.ro_flag) + err_exit("Device '%s' is mounted. " + "You must 'umount' it first.\n", opt.volume); + } + vol = check_volume(); + + if (vol->flags & VOLUME_IS_DIRTY) + if (opt.force-- <= 0) + err_exit("Volume is scheduled for check.\nRun chkdsk /f" + " and please try again, or see option -f.\n"); + + if (NTFS_MAX_CLUSTER_SIZE < vol->cluster_size) + err_exit("Cluster size %u is too large!\n", + (unsigned int)vol->cluster_size); + + if (ntfs_volume_get_free_space(vol)) + err_exit("Failed to update the free space\n"); + + if (!opt.infombonly) { + printf("Device name : %s\n", opt.volume); + printf("NTFS volume version: %d.%d\n", + vol->major_ver, vol->minor_ver); + } + if (ntfs_version_is_supported(vol)) + perr_exit("Unknown NTFS version"); + + if (!opt.infombonly) { + printf("Cluster size : %u bytes\n", + (unsigned int)vol->cluster_size); + print_vol_size("Current volume size", + vol_size(vol, vol->nr_clusters)); + } + + return vol; +} + +/** + * prepare_volume_fixup + * + * Set the volume's dirty flag and wipe the filesystem journal. When Windows + * boots it will automatically run chkdsk to check for any problems. If the + * read-only command line option was given, this function will do nothing. + */ +static void prepare_volume_fixup(ntfs_volume *vol) +{ + printf("Schedule chkdsk for NTFS consistency check at Windows boot " + "time ...\n"); + vol->flags |= VOLUME_IS_DIRTY; + if (ntfs_volume_write_flags(vol, vol->flags)) + perr_exit("Failed to set the volume dirty"); + + /* Porting note: This flag does not exist in libntfs-3g. The dirty flag + * is never modified by libntfs-3g on unmount and we set it above. We + * can safely comment out this statement. */ + /* NVolSetWasDirty(vol); */ + + if (vol->dev->d_ops->sync(vol->dev) == -1) + perr_exit("Failed to sync device"); + printf("Resetting $LogFile ... (this might take a while)\n"); + if (ntfs_logfile_reset(vol)) + perr_exit("Failed to reset $LogFile"); + if (vol->dev->d_ops->sync(vol->dev) == -1) + perr_exit("Failed to sync device"); +} + +static void set_disk_usage_constraint(ntfs_resize_t *resize) +{ + /* last lcn for a filled up volume (no empty space) */ + s64 last = resize->inuse - 1; + + if (resize->last_unsupp < last) + resize->last_unsupp = last; +} + +static void check_resize_constraints(ntfs_resize_t *resize) +{ + s64 new_size = resize->new_volume_size; + + /* FIXME: resize.shrink true also if only -i is used */ + if (!resize->shrink) + return; + + if (resize->inuse == resize->vol->nr_clusters) + err_exit("Volume is full. To shrink it, " + "delete unused files.\n"); + + if (opt.info || opt.infombonly) + return; + + /* FIXME: reserve some extra space so Windows can boot ... */ + if (new_size < resize->inuse) + err_exit("New size can't be less than the space already" + " occupied by data.\nYou either need to delete unused" + " files or see the -i option.\n"); + + if (new_size <= resize->last_unsupp) + err_exit("The fragmentation type, you have, isn't " + "supported yet. Rerun ntfsresize\nwith " + "the -i option to estimate the smallest " + "shrunken volume size supported.\n"); + + print_num_of_relocations(resize); +} + +static void check_cluster_allocation(ntfs_volume *vol, ntfsck_t *fsck) +{ + memset(fsck, 0, sizeof(ntfsck_t)); + + if (opt.show_progress) + fsck->flags |= NTFSCK_PROGBAR; + + if (setup_lcn_bitmap(&fsck->lcn_bitmap, vol->nr_clusters) != 0) + perr_exit("Failed to setup allocation bitmap"); + if (build_allocation_bitmap(vol, fsck) != 0) + exit(1); + if (fsck->outsider || fsck->multi_ref) { + err_printf("Filesystem check failed!\n"); + if (fsck->outsider) + err_printf("%d clusters are referenced outside " + "of the volume.\n", fsck->outsider); + if (fsck->multi_ref) + err_printf("%d clusters are referenced multiple" + " times.\n", fsck->multi_ref); + printf("%s", corrupt_volume_msg); + exit(1); + } + + compare_bitmaps(vol, &fsck->lcn_bitmap); +} + +/* + * Following are functions to expand an NTFS file system + * to the beginning of a partition. The old metadata can be + * located according to the backup bootsector, provided it can + * still be found at the end of the partition. + * + * The data itself is kept in place, and this is only possible + * if the expanded size is a multiple of cluster size, and big + * enough to hold the new $Boot, $Bitmap and $MFT + * + * The volume cannot be mounted because the layout of data does + * not match the volume parameters. The alignments of MFT entries + * and index blocks may be different in the new volume and the old + * one. The "ntfs_volume" structure is only partially usable, + * "ntfs_inode" and "search_context" cannot be used until the + * metadata has been moved and the volume is opened. + * + * Currently, no part of this new code is called from old code, + * and the only change in old code is the processing of options. + * Deduplication of code should be done later when the code is + * proved safe. + * + */ + +typedef struct EXPAND { + ntfs_volume *vol; + u64 original_sectors; + u64 new_sectors; + u64 bitmap_allocated; + u64 bitmap_size; + u64 boot_size; + u64 mft_size; + LCN mft_lcn; + s64 byte_increment; + s64 sector_increment; + s64 cluster_increment; + u8 *bitmap; + u8 *mft_bitmap; + char *bootsector; + MFT_RECORD *mrec; + struct progress_bar *progress; + struct DELAYED *delayed_runlists; /* runlists to process later */ +} expand_t; + +/* + * Locate an attribute in an MFT record + * + * Returns NULL if not found (with no error message) + */ + +static ATTR_RECORD *find_attr(MFT_RECORD *mrec, ATTR_TYPES type, + ntfschar *name, int namelen) +{ + ATTR_RECORD *a; + u32 offset; + ntfschar *attrname; + + /* fetch the requested attribute */ + offset = le16_to_cpu(mrec->attrs_offset); + a = (ATTR_RECORD*)((char*)mrec + offset); + attrname = (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)); + while ((a->type != AT_END) + && ((a->type != type) + || (a->name_length != namelen) + || (namelen && memcmp(attrname,name,2*namelen))) + && (offset < le32_to_cpu(mrec->bytes_in_use))) { + offset += le32_to_cpu(a->length); + a = (ATTR_RECORD*)((char*)mrec + offset); + if (namelen) + attrname = (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)); + } + if ((a->type != type) + || (a->name_length != namelen) + || (namelen && memcmp(attrname,name,2*namelen))) + a = (ATTR_RECORD*)NULL; + return (a); +} + +/* + * Read an MFT record and find an unnamed attribute + * + * Returns NULL if fails to read or attribute is not found + */ + +static ATTR_RECORD *get_unnamed_attr(expand_t *expand, ATTR_TYPES type, + s64 inum) +{ + ntfs_volume *vol; + ATTR_RECORD *a; + MFT_RECORD *mrec; + s64 pos; + BOOL found; + int got; + + found = FALSE; + a = (ATTR_RECORD*)NULL; + mrec = expand->mrec; + vol = expand->vol; + pos = (vol->mft_lcn << vol->cluster_size_bits) + + (inum << vol->mft_record_size_bits) + + expand->byte_increment; + got = ntfs_mst_pread(vol->dev, pos, 1, vol->mft_record_size, mrec); + if ((got == 1) && (mrec->flags & MFT_RECORD_IN_USE)) { + a = find_attr(expand->mrec, type, NULL, 0); + found = a && (a->type == type) && !a->name_length; + } + /* not finding the attribute list is not an error */ + if (!found && (type != AT_ATTRIBUTE_LIST)) { + err_printf("Could not find attribute 0x%lx in inode %lld\n", + (long)le32_to_cpu(type), (long long)inum); + a = (ATTR_RECORD*)NULL; + } + return (a); +} + +/* + * Read an MFT record and find an unnamed attribute + * + * Returns NULL if fails + */ + +static ATTR_RECORD *read_and_get_attr(expand_t *expand, ATTR_TYPES type, + s64 inum, ntfschar *name, int namelen) +{ + ntfs_volume *vol; + ATTR_RECORD *a; + MFT_RECORD *mrec; + s64 pos; + int got; + + a = (ATTR_RECORD*)NULL; + mrec = expand->mrec; + vol = expand->vol; + pos = (vol->mft_lcn << vol->cluster_size_bits) + + (inum << vol->mft_record_size_bits) + + expand->byte_increment; + got = ntfs_mst_pread(vol->dev, pos, 1, vol->mft_record_size, mrec); + if ((got == 1) && (mrec->flags & MFT_RECORD_IN_USE)) { + a = find_attr(expand->mrec, type, name, namelen); + } + if (!a) { + err_printf("Could not find attribute 0x%lx in inode %lld\n", + (long)le32_to_cpu(type), (long long)inum); + } + return (a); +} + +/* + * Get the size allocated to the unnamed data of some inode + * + * Returns zero if fails. + */ + +static s64 get_data_size(expand_t *expand, s64 inum) +{ + ATTR_RECORD *a; + s64 size; + + size = 0; + /* get the size of unnamed $DATA */ + a = get_unnamed_attr(expand, AT_DATA, inum); + if (a && a->non_resident) + size = le64_to_cpu(a->allocated_size); + if (!size) { + err_printf("Bad record %lld, could not get its size\n", + (long long)inum); + } + return (size); +} + +/* + * Get the MFT bitmap + * + * Returns NULL if fails. + */ + +static u8 *get_mft_bitmap(expand_t *expand) +{ + ATTR_RECORD *a; + ntfs_volume *vol; + runlist_element *rl; + runlist_element *prl; + u32 bitmap_size; + BOOL ok; + + expand->mft_bitmap = (u8*)NULL; + vol = expand->vol; + /* get the runlist of unnamed bitmap */ + a = get_unnamed_attr(expand, AT_BITMAP, FILE_MFT); + ok = TRUE; + bitmap_size = le64_to_cpu(a->allocated_size); + if (a + && a->non_resident + && ((bitmap_size << (vol->mft_record_size_bits + 3)) + >= expand->mft_size)) { +// rl in extent not implemented + rl = ntfs_mapping_pairs_decompress(expand->vol, a, + (runlist_element*)NULL); + expand->mft_bitmap = (u8*)ntfs_calloc(bitmap_size); + if (rl && expand->mft_bitmap) { + for (prl=rl; prl->length && ok; prl++) { + lseek_to_cluster(vol, + prl->lcn + expand->cluster_increment); + ok = !read_all(vol->dev, expand->mft_bitmap + + (prl->vcn << vol->cluster_size_bits), + prl->length << vol->cluster_size_bits); + } + if (!ok) { + err_printf("Could not read the MFT bitmap\n"); + free(expand->mft_bitmap); + expand->mft_bitmap = (u8*)NULL; + } + free(rl); + } else { + err_printf("Could not get the MFT bitmap\n"); + } + } else + err_printf("Invalid MFT bitmap\n"); + return (expand->mft_bitmap); +} + +/* + * Check for bad sectors + * + * Deduplication to be done when proved safe + */ + +static int check_expand_bad_sectors(expand_t *expand, ATTR_RECORD *a) +{ + runlist *rl; + int res; + s64 i, badclusters = 0; + + res = 0; + ntfs_log_verbose("Checking for bad sectors ...\n"); + + if (find_attr(expand->mrec, AT_ATTRIBUTE_LIST, NULL, 0)) { + err_printf("Hopelessly many bad sectors have been detected!\n"); + err_printf("%s", many_bad_sectors_msg); + res = -1; + } else { + + /* + * FIXME: The below would be partial for non-base records in the + * not yet supported multi-record case. Alternatively use audited + * ntfs_attr_truncate after an umount & mount. + */ + rl = ntfs_mapping_pairs_decompress(expand->vol, a, NULL); + if (!rl) { + perr_printf("Decompressing $BadClust:" + "$Bad mapping pairs failed"); + res = -1; + } else { + for (i = 0; rl[i].length; i++) { + /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ + if (rl[i].lcn == LCN_HOLE + || rl[i].lcn == LCN_RL_NOT_MAPPED) + continue; + + badclusters += rl[i].length; + ntfs_log_verbose("Bad cluster: %#8llx - %#llx" + " (%lld)\n", + (long long)rl[i].lcn, + (long long)rl[i].lcn + + rl[i].length - 1, + (long long)rl[i].length); + } + + if (badclusters) { + err_printf("%sThis software has detected that" + " the disk has at least" + " %lld bad sector%s.\n", + !opt.badsectors ? NERR_PREFIX + : "WARNING: ", + (long long)badclusters, + badclusters - 1 ? "s" : ""); + if (!opt.badsectors) { + err_printf("%s", bad_sectors_warning_msg); + res = -1; + } else + err_printf("WARNING: Bad sectors can cause" + " reliability problems" + " and massive data loss!!!\n"); + } + free(rl); + } + } + return (res); +} + +/* + * Check miscellaneous expansion constraints + */ + +static int check_expand_constraints(expand_t *expand) +{ + static ntfschar bad[] = { + const_cpu_to_le16('$'), const_cpu_to_le16('B'), + const_cpu_to_le16('a'), const_cpu_to_le16('d') + } ; + ATTR_RECORD *a; + runlist_element *rl; + VOLUME_INFORMATION *volinfo; + VOLUME_FLAGS flags; + int res; + + if (opt.verbose) + ntfs_log_verbose("Checking for expansion constraints...\n"); + res = 0; + /* extents for $MFT are not supported */ + if (get_unnamed_attr(expand, AT_ATTRIBUTE_LIST, FILE_MFT)) { + err_printf("The $MFT is too much fragmented\n"); + res = -1; + } + /* fragmented $MFTMirr is not supported */ + a = get_unnamed_attr(expand, AT_DATA, FILE_MFTMirr); + if (a) { + rl = ntfs_mapping_pairs_decompress(expand->vol, a, NULL); + if (!rl || !rl[0].length || rl[1].length) { + err_printf("$MFTMirr is bad or fragmented\n"); + res = -1; + } + free(rl); + } + /* fragmented $Boot is not supported */ + a = get_unnamed_attr(expand, AT_DATA, FILE_Boot); + if (a) { + rl = ntfs_mapping_pairs_decompress(expand->vol, a, NULL); + if (!rl || !rl[0].length || rl[1].length) { + err_printf("$Boot is bad or fragmented\n"); + res = -1; + } + free(rl); + } + /* Volume should not be marked dirty */ + a = get_unnamed_attr(expand, AT_VOLUME_INFORMATION, FILE_Volume); + if (a) { + volinfo = (VOLUME_INFORMATION*) + (le16_to_cpu(a->value_offset) + (char*)a); + flags = volinfo->flags; + if ((flags & VOLUME_IS_DIRTY) && (opt.force-- <= 0)) { + err_printf("Volume is scheduled for check.\nRun chkdsk /f" + " and please try again, or see option -f.\n"); + res = -1; + } + } else { + err_printf("Could not get Volume flags\n"); + res = -1; + } + + /* There should not be too many bad clusters */ + a = read_and_get_attr(expand, AT_DATA, FILE_BadClus, bad, 4); + if (!a || !a->non_resident) { + err_printf("Resident attribute in $BadClust! Please report to " + "%s\n", NTFS_DEV_LIST); + res = -1; + } else + if (check_expand_bad_sectors(expand,a)) + res = -1; + return (res); +} + +/* + * Compute the new sizes and check whether the NTFS file + * system can be expanded + * + * The partition has to have been expanded, + * the extra space must be able to hold the $MFT, $Boot, and $Bitmap + * the extra space must be a multiple of cluster size + * + * Returns TRUE if the partition can be expanded, + * FALSE if it canno be expanded or option --info was set + */ + +static BOOL can_expand(expand_t *expand, ntfs_volume *vol) +{ + s64 old_sector_count; + s64 sectors_needed; + s64 clusters; + s64 minimum_size; + s64 got; + s64 advice; + s64 bitmap_bits; + BOOL ok; + + ok = TRUE; + old_sector_count = vol->nr_clusters + << (vol->cluster_size_bits - vol->sector_size_bits); + /* do not include the space lost near the end */ + expand->cluster_increment = (expand->new_sectors + >> (vol->cluster_size_bits - vol->sector_size_bits)) + - vol->nr_clusters; + expand->byte_increment = expand->cluster_increment + << vol->cluster_size_bits; + expand->sector_increment = expand->byte_increment + >> vol->sector_size_bits; + printf("Sectors allocated to volume : old %lld current %lld difference %lld\n", + (long long)old_sector_count, + (long long)(old_sector_count + expand->sector_increment), + (long long)expand->sector_increment); + printf("Clusters allocated to volume : old %lld current %lld difference %lld\n", + (long long)vol->nr_clusters, + (long long)(vol->nr_clusters + + expand->cluster_increment), + (long long)expand->cluster_increment); + /* the new size must be bigger */ + if ((expand->sector_increment < 0) + || (!expand->sector_increment && !opt.info)) { + err_printf("Cannot expand volume : the partition has not been expanded\n"); + ok = FALSE; + } + /* the old bootsector must match the backup */ + got = ntfs_pread(expand->vol->dev, expand->byte_increment, + vol->sector_size, expand->mrec); + if ((got != vol->sector_size) + || memcmp(expand->bootsector,expand->mrec,vol->sector_size)) { + err_printf("The backup bootsector does not match the old bootsector\n"); + ok = FALSE; + } + if (ok) { + /* read the first MFT record, to get the MFT size */ + expand->mft_size = get_data_size(expand, FILE_MFT); + /* read the 6th MFT record, to get the $Boot size */ + expand->boot_size = get_data_size(expand, FILE_Boot); + if (!expand->mft_size || !expand->boot_size) { + ok = FALSE; + } else { + /* + * The bitmap is one bit per full cluster, + * accounting for the backup bootsector. + * When evaluating the minimal size, the bitmap + * size must be adapted to the minimal size : + * bits = clusters + ceil(clusters/clustersize) + */ + if (opt.info) { + clusters = (((expand->original_sectors + 1) + << vol->sector_size_bits) + + expand->mft_size + + expand->boot_size) + >> vol->cluster_size_bits; + bitmap_bits = ((clusters + 1) + << vol->cluster_size_bits) + / (vol->cluster_size + 1); + } else { + bitmap_bits = (expand->new_sectors + 1) + >> (vol->cluster_size_bits + - vol->sector_size_bits); + } + /* byte size must be a multiple of 8 */ + expand->bitmap_size = ((bitmap_bits + 63) >> 3) & -8; + expand->bitmap_allocated = ((expand->bitmap_size - 1) + | (vol->cluster_size - 1)) + 1; + expand->mft_lcn = (expand->boot_size + + expand->bitmap_allocated) + >> vol->cluster_size_bits; + /* + * Check whether $Boot, $Bitmap and $MFT can fit + * into the expanded space. + */ + sectors_needed = (expand->boot_size + expand->mft_size + + expand->bitmap_allocated) + >> vol->sector_size_bits; + if (!opt.info + && (sectors_needed >= expand->sector_increment)) { + err_printf("The expanded space cannot hold the new metadata\n"); + err_printf(" expanded space %lld sectors\n", + (long long)expand->sector_increment); + err_printf(" needed space %lld sectors\n", + (long long)sectors_needed); + ok = FALSE; + } + } + } + if (ok) { + advice = expand->byte_increment; + /* the increment must be an integral number of clusters */ + if (expand->byte_increment & (vol->cluster_size - 1)) { + err_printf("Cannot expand volume without copying the data :\n"); + err_printf("There are %d sectors in a cluster,\n", + (int)(vol->cluster_size/vol->sector_size)); + err_printf(" and the sector difference is not a multiple of %d\n", + (int)(vol->cluster_size/vol->sector_size)); + advice = expand->byte_increment & ~vol->cluster_size; + ok = FALSE; + } + if (!ok) + err_printf("You should increase the beginning of partition by %d sectors\n", + (int)((expand->byte_increment - advice) + >> vol->sector_size_bits)); + } + if (ok) + ok = !check_expand_constraints(expand); + if (ok && opt.info) { + minimum_size = (expand->original_sectors + << vol->sector_size_bits) + + expand->boot_size + + expand->mft_size + + expand->bitmap_allocated; + + printf("You must expand the partition to at least %lld bytes,\n", + (long long)(minimum_size + vol->sector_size)); + printf("and you may add a multiple of %ld bytes to this size.\n", + (long)vol->cluster_size); + printf("The minimum NTFS volume size is %lld bytes\n", + (long long)minimum_size); + ok = FALSE; + } + return (ok); +} + +static int set_bitmap(expand_t *expand, runlist_element *rl) +{ + int res; + s64 lcn; + s64 lcn_end; + BOOL reallocated; + + res = -1; + reallocated = FALSE; + if ((rl->lcn >= 0) + && (rl->length > 0) + && ((rl->lcn + rl->length) + <= (expand->vol->nr_clusters + expand->cluster_increment))) { + lcn = rl->lcn; + lcn_end = lcn + rl->length; + while ((lcn & 7) && (lcn < lcn_end)) { + if (expand->bitmap[lcn >> 3] & 1 << (lcn & 7)) + reallocated = TRUE; + expand->bitmap[lcn >> 3] |= 1 << (lcn & 7); + lcn++; + } + while ((lcn_end - lcn) >= 8) { + if (expand->bitmap[lcn >> 3]) + reallocated = TRUE; + expand->bitmap[lcn >> 3] = 255; + lcn += 8; + } + while (lcn < lcn_end) { + if (expand->bitmap[lcn >> 3] & 1 << (lcn & 7)) + reallocated = TRUE; + expand->bitmap[lcn >> 3] |= 1 << (lcn & 7); + lcn++; + } + if (reallocated) + err_printf("Reallocated cluster found in run" + " lcn 0x%llx length %lld\n", + (long long)rl->lcn,(long long)rl->length); + else + res = 0; + } else { + err_printf("Bad run : lcn 0x%llx length %lld\n", + (long long)rl->lcn,(long long)rl->length); + } + return (res); +} + +/* + * Write the backup bootsector + * + * When this has been done, the resizing cannot be done again + */ + +static int write_bootsector(expand_t *expand) +{ + ntfs_volume *vol; + s64 bw; + int res; + + res = -1; + vol = expand->vol; + if (opt.verbose) + ntfs_log_verbose("Rewriting the backup bootsector\n"); + if (opt.ro_flag) + bw = vol->sector_size; + else + bw = ntfs_pwrite(vol->dev, + expand->new_sectors*vol->sector_size, + vol->sector_size, expand->bootsector); + if (bw == vol->sector_size) + res = 0; + else { + if (bw != -1) + errno = EINVAL; + if (!bw) + err_printf("Failed to rewrite the bootsector (size=0)\n"); + else + err_printf("Error rewriting the bootsector"); + } + return (res); +} + +/* + * Write the new main bitmap + */ + +static int write_bitmap(expand_t *expand) +{ + ntfs_volume *vol; + s64 bw; + u64 cluster; + int res; + + res = -1; + vol = expand->vol; + cluster = vol->nr_clusters + expand->cluster_increment; + while (cluster < (expand->bitmap_size << 3)) { + expand->bitmap[cluster >> 3] |= 1 << (cluster & 7); + cluster++; + } + if (opt.verbose) + ntfs_log_verbose("Writing the new bitmap...\n"); + /* write the full allocation (to avoid having to read) */ + if (opt.ro_flag) + bw = expand->bitmap_allocated; + else + bw = ntfs_pwrite(vol->dev, expand->boot_size, + expand->bitmap_allocated, expand->bitmap); + if (bw == (s64)expand->bitmap_allocated) + res = 0; + else { + if (bw != -1) + errno = EINVAL; + if (!bw) + err_printf("Failed to write the bitmap (size=0)\n"); + else + err_printf("Error rewriting the bitmap"); + } + return (res); +} + +/* + * Copy the $MFT to $MFTMirr + * + * The $MFTMirr is not relocated as it should be kept away from $MFT. + * Apart from the backup bootsector, this is the only part which is + * overwritten. This has no effect on being able to redo the resizing + * if something goes wrong, as the $MFTMirr is never read. However + * this is done near the end of the resizing. + */ + +static int copy_mftmirr(expand_t *expand) +{ + ntfs_volume *vol; + s64 pos; + s64 inum; + int res; + u16 usa_ofs; + le16 *pusn; + u16 usn; + + if (opt.verbose) + ntfs_log_verbose("Copying $MFT to $MFTMirr...\n"); + vol = expand->vol; + res = 0; + for (inum=FILE_MFT; !res && (inum<=FILE_Volume); inum++) { + /* read the new $MFT */ + pos = (expand->mft_lcn << vol->cluster_size_bits) + + (inum << vol->mft_record_size_bits); + if (ntfs_mst_pread(vol->dev, pos, 1, vol->mft_record_size, + expand->mrec) == 1) { + /* overwrite the old $MFTMirr */ + pos = (vol->mftmirr_lcn << vol->cluster_size_bits) + + (inum << vol->mft_record_size_bits) + + expand->byte_increment; + usa_ofs = le16_to_cpu(expand->mrec->usa_ofs); + pusn = (le16*)((u8*)expand->mrec + usa_ofs); + usn = le16_to_cpu(*pusn) - 1; + if (!usn || (usn == 0xffff)) + usn = -2; + *pusn = cpu_to_le16(usn); + if (!opt.ro_flag + && (ntfs_mst_pwrite(vol->dev, pos, 1, + vol->mft_record_size, expand->mrec) != 1)) { + err_printf("Failed to overwrite the old $MFTMirr\n"); + res = -1; + } + } else { + err_printf("Failed to write the new $MFT\n"); + res = -1; + } + } + return (res); +} + +/* + * Copy the $Boot, including the bootsector + * + * When the bootsector has been copied, repair tools are able to + * fix things, but this is dangerous if the other metadata do + * not point to actual user data. So this must be done near the end + * of resizing. + */ + +static int copy_boot(expand_t *expand) +{ + NTFS_BOOT_SECTOR *bs; + char *buf; + ntfs_volume *vol; + s64 mftmirr_lcn; + s64 written; + u32 boot_cnt; + u32 hidden_sectors; + le32 hidden_sectors_le; + int res; + + if (opt.verbose) + ntfs_log_verbose("Copying $Boot...\n"); + vol = expand->vol; + res = 0; + buf = (char*)ntfs_malloc(vol->cluster_size); + if (buf) { + /* set the new volume parameters in the bootsector */ + bs = (NTFS_BOOT_SECTOR*)expand->bootsector; + bs->number_of_sectors = cpu_to_le64(expand->new_sectors); + bs->mft_lcn = cpu_to_le64(expand->mft_lcn); + mftmirr_lcn = vol->mftmirr_lcn + expand->cluster_increment; + bs->mftmirr_lcn = cpu_to_le64(mftmirr_lcn); + /* the hidden sectors are needed to boot into windows */ + memcpy(&hidden_sectors_le,&bs->bpb.hidden_sectors,4); + /* alignment messed up on the Sparc */ + if (hidden_sectors_le) { + hidden_sectors = le32_to_cpu(hidden_sectors_le); + if (hidden_sectors >= expand->sector_increment) + hidden_sectors -= expand->sector_increment; + else + hidden_sectors = 0; + hidden_sectors_le = cpu_to_le32(hidden_sectors); + memcpy(&bs->bpb.hidden_sectors,&hidden_sectors_le,4); + } + written = 0; + boot_cnt = expand->boot_size >> vol->cluster_size_bits; + while (!res && (written < boot_cnt)) { + lseek_to_cluster(vol, expand->cluster_increment + written); + if (!read_all(vol->dev, buf, vol->cluster_size)) { + if (!written) + memcpy(buf, expand->bootsector, vol->sector_size); + lseek_to_cluster(vol, written); + if (!opt.ro_flag + && write_all(vol->dev, buf, vol->cluster_size)) { + err_printf("Failed to write the new $Boot\n"); + res = -1; + } else + written++; + } else { + err_printf("Failed to read the old $Boot\n"); + res = -1; + } + } + free(buf); + } else { + err_printf("Failed to allocate buffer\n"); + res = -1; + } + return (res); +} + +/* + * Process delayed runlist updates + * + * This is derived from delayed_updates() and they should + * both be merged when the new code is considered safe. + */ + +static void delayed_expand(ntfs_volume *vol, struct DELAYED *delayed, + struct progress_bar *progress) +{ + unsigned long count; + struct DELAYED *current; + int step = 100; + + if (delayed) { + if (opt.verbose) + ntfs_log_verbose("Delayed updating of overflowing runlists...\n"); + count = 0; + /* count by steps because of inappropriate resolution */ + for (current=delayed; current; current=current->next) + count += step; + progress_init(progress, 0, count, + (opt.show_progress ? NTFS_PROGBAR : 0)); + current = delayed; + count = 0; + while (current) { + delayed = current; + if (!opt.ro_flag) + expand_attribute_runlist(vol, delayed); + count += step; + progress_update(progress, count); + current = current->next; + if (delayed->attr_name) + free(delayed->attr_name); + free(delayed->head_rl); + free(delayed); + } + } +} + +/* + * Expand the sizes in indexes for inodes which were expanded + * + * Only the new $Bitmap sizes are identified as needed to be + * adjusted in index. The $BadClus is only expanded in an + * alternate data stream, whose sizes are not present in the index. + * + * This is modifying the initial data, and can only be done when + * the volume has been reopened after expanding. + */ + +static int expand_index_sizes(expand_t *expand) +{ + ntfs_inode *ni; + int res; + + res = -1; + ni = ntfs_inode_open(expand->vol, FILE_Bitmap); + if (ni) { + NInoSetDirty(ni); + NInoFileNameSetDirty(ni); + ntfs_inode_close(ni); + res = 0; + } + return (res); +} + +/* + * Update a runlist into an attribute + * + * This is derived from replace_attribute_runlist() and they should + * both be merged when the new code is considered safe. + */ + +static int update_runlist(expand_t *expand, s64 inum, + ATTR_RECORD *a, runlist_element *rl) +{ + ntfs_resize_t resize; + ntfs_attr_search_ctx ctx; + ntfs_volume *vol; + MFT_RECORD *mrec; + runlist *head_rl; + int mp_size; + int l; + int must_delay; + void *mp; + + vol = expand->vol; + mrec = expand->mrec; + head_rl = rl; + rl_fixup(&rl); + if ((mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, + 0, INT_MAX)) == -1) + perr_exit("ntfs_get_size_for_mapping_pairs"); + + if (a->name_length) { + u16 name_offs = le16_to_cpu(a->name_offset); + u16 mp_offs = le16_to_cpu(a->mapping_pairs_offset); + + if (name_offs >= mp_offs) + err_exit("Attribute name is after mapping pairs! " + "Please report!\n"); + } + + /* CHECKME: don't trust mapping_pairs is always the last item in the + attribute, instead check for the real size/space */ + l = (int)le32_to_cpu(a->length) - le16_to_cpu(a->mapping_pairs_offset); + must_delay = 0; + if (mp_size > l) { + s32 remains_size; + char *next_attr; + + ntfs_log_verbose("Enlarging attribute header ...\n"); + + mp_size = (mp_size + 7) & ~7; + + ntfs_log_verbose("Old mp size : %d\n", l); + ntfs_log_verbose("New mp size : %d\n", mp_size); + ntfs_log_verbose("Bytes in use : %u\n", (unsigned int) + le32_to_cpu(mrec->bytes_in_use)); + + next_attr = (char *)a + le32_to_cpu(a->length); + l = mp_size - l; + + ntfs_log_verbose("Bytes in use new : %u\n", l + (unsigned int) + le32_to_cpu(mrec->bytes_in_use)); + ntfs_log_verbose("Bytes allocated : %u\n", (unsigned int) + le32_to_cpu(mrec->bytes_allocated)); + + remains_size = le32_to_cpu(mrec->bytes_in_use); + remains_size -= (next_attr - (char *)mrec); + + ntfs_log_verbose("increase : %d\n", l); + ntfs_log_verbose("shift : %lld\n", + (long long)remains_size); + if (le32_to_cpu(mrec->bytes_in_use) + l > + le32_to_cpu(mrec->bytes_allocated)) { + ntfs_log_verbose("Queuing expansion for later processing\n"); + /* hack for reusing unmodified old code ! */ + resize.ctx = &ctx; + ctx.attr = a; + ctx.mrec = mrec; + resize.mref = inum; + resize.delayed_runlists = expand->delayed_runlists; + must_delay = 1; + replace_later(&resize,rl,head_rl); + expand->delayed_runlists = resize.delayed_runlists; + } else { + memmove(next_attr + l, next_attr, remains_size); + mrec->bytes_in_use = cpu_to_le32(l + + le32_to_cpu(mrec->bytes_in_use)); + a->length = cpu_to_le32(le32_to_cpu(a->length) + l); + } + } + + if (!must_delay) { + mp = ntfs_calloc(mp_size); + if (!mp) + perr_exit("ntfsc_calloc couldn't get memory"); + + if (ntfs_mapping_pairs_build(vol, (u8*)mp, mp_size, rl, 0, NULL)) + perr_exit("ntfs_mapping_pairs_build"); + + memmove((u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp, mp_size); + + free(mp); + } + return (must_delay); +} + +/* + * Create a minimal valid MFT record + */ + +static int minimal_record(expand_t *expand, MFT_RECORD *mrec) +{ + int usa_count; + u32 bytes_in_use; + + memset(mrec,0,expand->vol->mft_record_size); + mrec->magic = magic_FILE; + mrec->usa_ofs = const_cpu_to_le16(sizeof(MFT_RECORD)); + usa_count = expand->vol->mft_record_size / NTFS_BLOCK_SIZE + 1; + mrec->usa_count = cpu_to_le16(usa_count); + bytes_in_use = (sizeof(MFT_RECORD) + 2*usa_count + 7) & -8; + memset(((char*)mrec) + bytes_in_use, 255, 4); /* AT_END */ + bytes_in_use += 8; + mrec->bytes_in_use = cpu_to_le32(bytes_in_use); + mrec->bytes_allocated = cpu_to_le32(expand->vol->mft_record_size); + return (0); +} + +/* + * Rebase all runlists of an MFT record + * + * Iterate through all its attributes and offset the non resident ones + */ + +static int rebase_runlists(expand_t *expand, s64 inum) +{ + MFT_RECORD *mrec; + ATTR_RECORD *a; + runlist_element *rl; + runlist_element *prl; + u32 offset; + int res; + + res = 0; + mrec = expand->mrec; + offset = le16_to_cpu(mrec->attrs_offset); + a = (ATTR_RECORD*)((char*)mrec + offset); + while (!res && (a->type != AT_END) + && (offset < le32_to_cpu(mrec->bytes_in_use))) { + if (a->non_resident) { + rl = ntfs_mapping_pairs_decompress(expand->vol, a, + (runlist_element*)NULL); + if (rl) { + for (prl=rl; prl->length; prl++) + if (prl->lcn >= 0) { + prl->lcn += expand->cluster_increment; + if (set_bitmap(expand,prl)) + res = -1; + } + if (update_runlist(expand,inum,a,rl)) { + ntfs_log_verbose("Runlist updating has to be delayed\n"); + } else + free(rl); + } else { + err_printf("Could not get a runlist of inode %lld\n", + (long long)inum); + res = -1; + } + } + offset += le32_to_cpu(a->length); + a = (ATTR_RECORD*)((char*)mrec + offset); + } + return (res); +} + +/* + * Rebase the runlists present in records with relocated $DATA + * + * The returned runlist is the old rebased runlist for $DATA, + * which is generally different from the new computed runlist. + */ + +static runlist_element *rebase_runlists_meta(expand_t *expand, s64 inum) +{ + MFT_RECORD *mrec; + ATTR_RECORD *a; + ntfs_volume *vol; + runlist_element *rl; + runlist_element *old_rl; + runlist_element *prl; + runlist_element new_rl[2]; + s64 data_size; + s64 allocated_size; + s64 lcn; + u64 lth; + u32 offset; + BOOL keeprl; + int res; + + res = 0; + old_rl = (runlist_element*)NULL; + vol = expand->vol; + mrec = expand->mrec; + switch (inum) { + case FILE_Boot : + lcn = 0; + lth = expand->boot_size >> vol->cluster_size_bits; + data_size = expand->boot_size; + break; + case FILE_Bitmap : + lcn = expand->boot_size >> vol->cluster_size_bits; + lth = expand->bitmap_allocated >> vol->cluster_size_bits; + data_size = expand->bitmap_size; + break; + case FILE_MFT : + lcn = (expand->boot_size + expand->bitmap_allocated) + >> vol->cluster_size_bits; + lth = expand->mft_size >> vol->cluster_size_bits; + data_size = expand->mft_size; + break; + case FILE_BadClus : + lcn = 0; /* not used */ + lth = vol->nr_clusters + expand->cluster_increment; + data_size = lth << vol->cluster_size_bits; + break; + default : + lcn = lth = data_size = 0; + res = -1; + } + allocated_size = lth << vol->cluster_size_bits; + offset = le16_to_cpu(mrec->attrs_offset); + a = (ATTR_RECORD*)((char*)mrec + offset); + while (!res && (a->type != AT_END) + && (offset < le32_to_cpu(mrec->bytes_in_use))) { + if (a->non_resident) { + keeprl = FALSE; + rl = ntfs_mapping_pairs_decompress(vol, a, + (runlist_element*)NULL); + if (rl) { + /* rebase the old runlist */ + for (prl=rl; prl->length; prl++) + if (prl->lcn >= 0) { + prl->lcn += expand->cluster_increment; + if ((a->type != AT_DATA) + && set_bitmap(expand,prl)) + res = -1; + } + /* relocated unnamed data (not $BadClus) */ + if ((a->type == AT_DATA) + && !a->name_length + && (inum != FILE_BadClus)) { + old_rl = rl; + rl = new_rl; + keeprl = TRUE; + rl[0].vcn = 0; + rl[0].lcn = lcn; + rl[0].length = lth; + rl[1].vcn = lth; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + if (set_bitmap(expand,rl)) + res = -1; + a->data_size = cpu_to_le64(data_size); + a->initialized_size = a->data_size; + a->allocated_size + = cpu_to_le64(allocated_size); + a->highest_vcn = cpu_to_le64(lth - 1); + } + /* expand the named data for $BadClus */ + if ((a->type == AT_DATA) + && a->name_length + && (inum == FILE_BadClus)) { + old_rl = rl; + keeprl = TRUE; + prl = rl; + if (prl->length) { + while (prl[1].length) + prl++; + prl->length = lth - prl->vcn; + prl[1].vcn = lth; + } else + prl->vcn = lth; + a->data_size = cpu_to_le64(data_size); + /* do not change the initialized size */ + a->allocated_size + = cpu_to_le64(allocated_size); + a->highest_vcn = cpu_to_le64(lth - 1); + } + if (!res && update_runlist(expand,inum,a,rl)) + res = -1; + if (!keeprl) + free(rl); + } else { + err_printf("Could not get the data runlist of inode %lld\n", + (long long)inum); + res = -1; + } + } + offset += le32_to_cpu(a->length); + a = (ATTR_RECORD*)((char*)mrec + offset); + } + if (res && old_rl) { + free(old_rl); + old_rl = (runlist_element*)NULL; + } + return (old_rl); +} + +/* + * Rebase all runlists in an MFT record + * + * Read from the old $MFT, rebase the runlists, + * and write to the new $MFT + */ + +static int rebase_inode(expand_t *expand, const runlist_element *prl, + s64 inum, s64 jnum) +{ + MFT_RECORD *mrec; + runlist_element *rl; + ntfs_volume *vol; + s64 pos; + int res; + + res = 0; + vol = expand->vol; + mrec = expand->mrec; + if (expand->mft_bitmap[inum >> 3] & (1 << (inum & 7))) { + pos = (prl->lcn << vol->cluster_size_bits) + + ((inum - jnum) << vol->mft_record_size_bits); + if ((ntfs_mst_pread(vol->dev, pos, 1, + vol->mft_record_size, mrec) == 1) + && (mrec->flags & MFT_RECORD_IN_USE)) { + switch (inum) { + case FILE_Bitmap : + case FILE_Boot : + case FILE_BadClus : + rl = rebase_runlists_meta(expand, inum); + if (rl) + free(rl); + else + res = -1; + break; + default : + res = rebase_runlists(expand, inum); + break; + } + } else { + err_printf("Could not read the $MFT entry %lld\n", + (long long)inum); + res = -1; + } + } else { + /* + * Replace unused records (possibly uninitialized) + * by minimal valid records, not marked in use + */ + res = minimal_record(expand,mrec); + } + if (!res) { + pos = (expand->mft_lcn << vol->cluster_size_bits) + + (inum << vol->mft_record_size_bits); + if (opt.verbose) + ntfs_log_verbose("Rebasing inode %lld cluster 0x%llx\n", + (long long)inum, + (long long)(pos >> vol->cluster_size_bits)); + if (!opt.ro_flag + && (ntfs_mst_pwrite(vol->dev, pos, 1, + vol->mft_record_size, mrec) != 1)) { + err_printf("Could not write the $MFT entry %lld\n", + (long long)inum); + res = -1; + } + } + return (res); +} + +/* + * Rebase all runlists + * + * First get the $MFT and define its location in the expanded space, + * then rebase the other inodes and write them to the new $MFT + */ + +static int rebase_all_inodes(expand_t *expand) +{ + ntfs_volume *vol; + MFT_RECORD *mrec; + s64 inum; + s64 jnum; + s64 inodecnt; + s64 pos; + s64 got; + int res; + runlist_element *mft_rl; + runlist_element *prl; + + res = 0; + mft_rl = (runlist_element*)NULL; + vol = expand->vol; + mrec = expand->mrec; + inum = 0; + pos = (vol->mft_lcn + expand->cluster_increment) + << vol->cluster_size_bits; + got = ntfs_mst_pread(vol->dev, pos, 1, + vol->mft_record_size, mrec); + if ((got == 1) && (mrec->flags & MFT_RECORD_IN_USE)) { + pos = expand->mft_lcn << vol->cluster_size_bits; + if (opt.verbose) + ntfs_log_verbose("Rebasing inode %lld cluster 0x%llx\n", + (long long)inum, + (long long)(pos >> vol->cluster_size_bits)); + mft_rl = rebase_runlists_meta(expand, FILE_MFT); + if (!mft_rl + || (!opt.ro_flag + && (ntfs_mst_pwrite(vol->dev, pos, 1, + vol->mft_record_size, mrec) != 1))) + res = -1; + else { + for (prl=mft_rl; prl->length; prl++) { } + inodecnt = (prl->vcn << vol->cluster_size_bits) + >> vol->mft_record_size_bits; + progress_init(expand->progress, 0, inodecnt, + (opt.show_progress ? NTFS_PROGBAR : 0)); + prl = mft_rl; + jnum = 0; + do { + inum++; + while (prl->length + && ((inum << vol->mft_record_size_bits) + >= ((prl->vcn + prl->length) + << vol->cluster_size_bits))) { + prl++; + jnum = inum; + } + progress_update(expand->progress, inum); + if (prl->length) { + res = rebase_inode(expand, + prl,inum,jnum); + } + } while (!res && prl->length); + free(mft_rl); + } + } else { + err_printf("Could not read the old $MFT\n"); + res = -1; + } + return (res); +} + + + +/* + * Get the old volume parameters from the backup bootsector + * + */ + +static ntfs_volume *get_volume_data(expand_t *expand, struct ntfs_device *dev, + s32 sector_size) +{ + s64 br; + ntfs_volume *vol; + le16 sector_size_le; + NTFS_BOOT_SECTOR *bs; + BOOL ok; + + ok = FALSE; + vol = (ntfs_volume*)ntfs_malloc(sizeof(ntfs_volume)); + expand->bootsector = (char*)ntfs_malloc(sector_size); + if (vol && expand->bootsector) { + expand->vol = vol; + vol->dev = dev; + br = ntfs_pread(dev, expand->new_sectors*sector_size, + sector_size, expand->bootsector); + if (br != sector_size) { + if (br != -1) + errno = EINVAL; + if (!br) + err_printf("Failed to read the backup bootsector (size=0)\n"); + else + err_printf("Error reading the backup bootsector"); + } else { + bs = (NTFS_BOOT_SECTOR*)expand->bootsector; + /* alignment problem on Sparc, even doing memcpy() */ + sector_size_le = cpu_to_le16(sector_size); + if (!memcmp(§or_size_le, + &bs->bpb.bytes_per_sector,2) + && ntfs_boot_sector_is_ntfs(bs) + && !ntfs_boot_sector_parse(vol, bs)) { + expand->original_sectors + = le64_to_cpu(bs->number_of_sectors); + expand->mrec = (MFT_RECORD*) + ntfs_malloc(vol->mft_record_size); + if (expand->mrec + && can_expand(expand,vol)) { + ntfs_log_verbose("Resizing is possible\n"); + ok = TRUE; + } + } else + err_printf("Could not get the old volume parameters " + "from the backup bootsector\n"); + } + if (!ok) { + free(vol); + free(expand->bootsector); + } + } + return (ok ? vol : (ntfs_volume*)NULL); +} + +static int really_expand(expand_t *expand) +{ + ntfs_volume *vol; + struct ntfs_device *dev; + int res; + + res = -1; + + expand->bitmap = (u8*)ntfs_calloc(expand->bitmap_allocated); + if (expand->bitmap + && get_mft_bitmap(expand)) { + printf("\n*** WARNING ***\n\n"); + printf("Expanding a volume is an experimental new feature\n"); + if (!opt.ro_flag) + printf("A first check with option -n is recommended\n"); + printf("\nShould something go wrong during the actual" + " resizing (power outage, etc.),\n"); + printf("just restart the procedure, but DO NOT TRY to repair" + " with chkdsk or similar,\n"); + printf("until the resizing is over," + " you would LOSE YOUR DATA !\n"); + printf("\nYou have been warned !\n\n"); + if (!opt.ro_flag && (opt.force-- <= 0)) + proceed_question(); + if (!rebase_all_inodes(expand) + && !write_bitmap(expand) + && !copy_mftmirr(expand) + && !copy_boot(expand)) { + free(expand->vol); + expand->vol = (ntfs_volume*)NULL; + free(expand->mft_bitmap); + expand->mft_bitmap = (u8*)NULL; + if (!opt.ro_flag) { + /* the volume must be dirty, do not check */ + opt.force++; + vol = mount_volume(); + if (vol) { + dev = vol->dev; + ntfs_log_verbose("Remounting the updated volume\n"); + expand->vol = vol; + ntfs_log_verbose("Delayed runlist updatings\n"); + delayed_expand(vol, expand->delayed_runlists, + expand->progress); + expand->delayed_runlists + = (struct DELAYED*)NULL; + expand_index_sizes(expand); + /* rewriting the backup bootsector, no return ticket now ! */ + res = write_bootsector(expand); + if (dev->d_ops->sync(dev) == -1) { + printf("Could not sync\n"); + res = -1; + } + ntfs_umount(vol,0); + if (!res) + printf("\nResizing completed successfully\n"); + } + } else { + ntfs_log_verbose("Delayed runlist updatings\n"); + delayed_expand(expand->vol, + expand->delayed_runlists, + expand->progress); + expand->delayed_runlists + = (struct DELAYED*)NULL; + printf("\nAll checks have been completed successfully\n"); + printf("Cannot check further in no-action mode\n"); + } + free(expand->bootsector); + free(expand->mrec); + } + free(expand->bitmap); + } else { + err_printf("Failed to allocate memory\n"); + } + return (res); +} + +/* + * Expand a volume to beginning of partition + * + * We rely on the backup bootsector to determine the original + * volume size and metadata. + */ + +static int expand_to_beginning(void) +{ + expand_t expand; + struct progress_bar progress; + int ret; + ntfs_volume *vol; + struct ntfs_device *dev; + int sector_size; + s64 new_sectors; + + ret = -1; + dev = ntfs_device_alloc(opt.volume, 0, &ntfs_device_default_io_ops, + NULL); + if (dev) { + if (!(*dev->d_ops->open)(dev, + (opt.ro_flag ? O_RDONLY : O_RDWR))) { + sector_size = ntfs_device_sector_size_get(dev); + if (sector_size <= 0) { + sector_size = 512; + new_sectors = ntfs_device_size_get(dev, + sector_size); + if (!new_sectors) { + sector_size = 4096; + new_sectors = ntfs_device_size_get(dev, + sector_size); + } + } else + new_sectors = ntfs_device_size_get(dev, + sector_size); + if (new_sectors) { + new_sectors--; /* last sector not counted */ + expand.new_sectors = new_sectors; + expand.progress = &progress; + expand.delayed_runlists = (struct DELAYED*)NULL; + vol = get_volume_data(&expand,dev,sector_size); + if (vol) { + expand.vol = vol; + ret = really_expand(&expand); + } + } + (*dev->d_ops->close)(dev); + } else { + err_exit("Couldn't open volume '%s'!\n", opt.volume); + } + ntfs_device_free(dev); + } + return (ret); +} + + +int main(int argc, char **argv) +{ + ntfsck_t fsck; + ntfs_resize_t resize; + s64 new_size = 0; /* in clusters; 0 = --info w/o --size */ + s64 device_size; /* in bytes */ + ntfs_volume *vol = NULL; + int res; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + printf("%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + + res = parse_options(argc, argv); + if (res >= 0) + return (res); + + utils_set_locale(); + + /* + * If we're just checking the device, we'll do it first, + * and exit out, no matter what we find. + */ + if (opt.check) { + vol = check_volume(); +#if CLEAN_EXIT + if (vol) + ntfs_umount(vol,0); +#endif + exit(0); + } else { + if (opt.expand) { + /* + * If we are to expand to beginning of partition, do + * not try to mount : when merging two partitions, + * the beginning of the partition would contain an + * old filesystem which is not the one to expand. + */ + if (expand_to_beginning() && !opt.info) + exit(1); + return (0); + } + } + + if (!(vol = mount_volume())) + err_exit("Couldn't open volume '%s'!\n", opt.volume); + + device_size = ntfs_device_size_get(vol->dev, vol->sector_size); + device_size *= vol->sector_size; + if (device_size <= 0) + err_exit("Couldn't get device size (%lld)!\n", + (long long)device_size); + + if (!opt.infombonly) + print_vol_size("Current device size", device_size); + + if (device_size < vol->nr_clusters * vol->cluster_size) + err_exit("Current NTFS volume size is bigger than the device " + "size!\nCorrupt partition table or incorrect device " + "partitioning?\n"); + + if (!opt.bytes && !opt.info && !opt.infombonly) { + opt.bytes = device_size; + opt.reliable_size = 1; + } + + /* Backup boot sector at the end of device isn't counted in NTFS + volume size thus we have to reserve space for it. */ + if (opt.bytes > vol->sector_size) + new_size = (opt.bytes - vol->sector_size) / vol->cluster_size; + else + new_size = 0; + + if (!opt.info && !opt.infombonly) { + print_vol_size("New volume size ", vol_size(vol, new_size)); + if (device_size < opt.bytes) + err_exit("New size can't be bigger than the device size" + ".\nIf you want to enlarge NTFS then first " + "enlarge the device size by e.g. fdisk.\n"); + } + + if (!opt.info && !opt.infombonly && (new_size == vol->nr_clusters || + (opt.bytes == device_size && + new_size == vol->nr_clusters - 1))) { + printf("Nothing to do: NTFS volume size is already OK.\n"); + exit(0); + } + + memset(&resize, 0, sizeof(resize)); + resize.vol = vol; + resize.new_volume_size = new_size; + /* This is also true if --info was used w/o --size (new_size = 0) */ + if (new_size < vol->nr_clusters) + resize.shrink = 1; + if (opt.show_progress) + resize.progress.flags |= NTFS_PROGBAR; + /* + * Checking and __reporting__ of bad sectors must be done before cluster + * allocation check because chkdsk doesn't fix $Bitmap's w/ bad sectors + * thus users would (were) quite confused why chkdsk doesn't work. + */ + resize.badclusters = check_bad_sectors(vol); + + NVolSetNoFixupWarn(vol); + check_cluster_allocation(vol, &fsck); + + print_disk_usage(vol, fsck.inuse); + + resize.inuse = fsck.inuse; + resize.lcn_bitmap = fsck.lcn_bitmap; + + set_resize_constraints(&resize); + set_disk_usage_constraint(&resize); + check_resize_constraints(&resize); + + if (opt.info || opt.infombonly) { + advise_on_resize(&resize); + exit(0); + } + + if (opt.force-- <= 0 && !opt.ro_flag) { + printf("%s", resize_warning_msg); + proceed_question(); + } + + /* FIXME: performance - relocate logfile here if it's needed */ + prepare_volume_fixup(vol); + + if (resize.relocations) + relocate_inodes(&resize); + + truncate_badclust_file(&resize); + truncate_bitmap_file(&resize); + delayed_updates(&resize); + update_bootsector(&resize); + + /* We don't create backup boot sector because we don't know where the + partition will be split. The scheduled chkdsk will fix it */ + + if (opt.ro_flag) { + printf("The read-only test run ended successfully.\n"); + exit(0); + } + + /* WARNING: don't modify the texts, external tools grep for them */ + printf("Syncing device ...\n"); + if (vol->dev->d_ops->sync(vol->dev) == -1) + perr_exit("fsync"); + + printf("Successfully resized NTFS on device '%s'.\n", vol->dev->d_name); + if (resize.shrink) + printf("%s", resize_important_msg); +#if CLEAN_EXIT + if (resize.lcn_bitmap.bm) + free(resize.lcn_bitmap.bm); + if (vol) + ntfs_umount(vol,0); +#endif + return 0; +} diff --git a/ntfsprogs/ntfstruncate.8 b/ntfsprogs/ntfstruncate.8 new file mode 100755 index 0000000000000000000000000000000000000000..9a75d538bc9c970049fe2147257b69083706fb94 --- /dev/null +++ b/ntfsprogs/ntfstruncate.8 @@ -0,0 +1,124 @@ +.\" Copyright (c) 2014 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSTRUNCATE 8 "June 2014" "ntfs-3g 2015.3.14" +.SH NAME +ntfstruncate \- truncate a file on an NTFS volume +.SH SYNOPSIS +\fBntfstruncate\fR [\fIoptions\fR] \fIdevice\fR \fIfile\fR \fI[attr-type\fR [\fIattr-name\fR]] \fInew-length\fR +.SH DESCRIPTION +.B ntfstruncate +truncates (or extends) a specified attribute belonging to a +file or directory, to a specified length. +.SH OPTIONS +Below is a summary of all the options that +.B ntfstruncate +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not using a mounted volume. +Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-l\fR +Display licensing information. +.TP +\fB\-n\fR, \fB\-\-no-action\fR +Simulate the truncation without actually write to device. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license of +.BR ntfstruncate . +.TP +\fBattr-type\fR +Define a particular attribute type to be truncated (advanced use only). +By default, the unnamed $DATA attribute (the contents of a plain file) will +be truncated. The attribute has to be specified by a number in decimal +or hexadecimal : +.TS +box; +lB lB lB +l l l. +Hex Decimal Name +0x10 16 "$STANDARD_INFORMATION" +0x20 32 "$ATTRIBUTE_LIST" +0x30 48 "$FILE_NAME" +0x40 64 "$OBJECT_ID" +0x50 80 "$SECURITY_DESCRIPTOR" +0x60 96 "$VOLUME_NAME" +0x70 112 "$VOLUME_INFORMATION" +0x80 128 "$DATA" +0x90 144 "$INDEX_ROOT" +0xA0 160 "$INDEX_ALLOCATION" +0xB0 176 "$BITMAP" +0xC0 192 "$REPARSE_POINT" +0xD0 208 "$EA_INFORMATION" +0xE0 224 "$EA" +0xF0 240 "$PROPERTY_SET" +0x100 256 "$LOGGED_UTILITY_STREAM" +.TE +.sp +.TP +\fBattr-name\fR +Define the name of the particular attribute type to be truncated +(advanced use only). +.sp +.TP +\fBnew-length\fR +Specify the target size of the file. +It will be rounded up to a multiple of the cluster size. +A suffix of K, M, G, T, P or E may be appended to +mean a multiplicative factor of a power of 1000. Similarly a suffix of +Ki, Mi, Gi, Ti, Pi or Ei may be appended to mean a multiplicative factor +of a power of 1024. +.SH EXAMPLES +Resize to 100MB the file database.db located in the Data directory +which is at the root of an NTFS file system. +.RS +.sp +.B ntfstruncate /dev/sda1 Data/database.db 100M +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfstruncate . +If you find a bug, please send an email +describing the problem to the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfstruncate +was written by Anton Altaparmakov. +.SH AVAILABILITY +.B ntfstruncate +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfs-3g (8), +.BR ntfsfallocate (8), +.BR ntfsprogs (8). diff --git a/ntfsprogs/ntfstruncate.8.in b/ntfsprogs/ntfstruncate.8.in new file mode 100755 index 0000000000000000000000000000000000000000..e793f649526f1262544cbaf03e89c2b337a41ab9 --- /dev/null +++ b/ntfsprogs/ntfstruncate.8.in @@ -0,0 +1,124 @@ +.\" Copyright (c) 2014 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSTRUNCATE 8 "June 2014" "ntfs-3g @VERSION@" +.SH NAME +ntfstruncate \- truncate a file on an NTFS volume +.SH SYNOPSIS +\fBntfstruncate\fR [\fIoptions\fR] \fIdevice\fR \fIfile\fR \fI[attr-type\fR [\fIattr-name\fR]] \fInew-length\fR +.SH DESCRIPTION +.B ntfstruncate +truncates (or extends) a specified attribute belonging to a +file or directory, to a specified length. +.SH OPTIONS +Below is a summary of all the options that +.B ntfstruncate +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not using a mounted volume. +Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-l\fR +Display licensing information. +.TP +\fB\-n\fR, \fB\-\-no-action\fR +Simulate the truncation without actually write to device. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license of +.BR ntfstruncate . +.TP +\fBattr-type\fR +Define a particular attribute type to be truncated (advanced use only). +By default, the unnamed $DATA attribute (the contents of a plain file) will +be truncated. The attribute has to be specified by a number in decimal +or hexadecimal : +.TS +box; +lB lB lB +l l l. +Hex Decimal Name +0x10 16 "$STANDARD_INFORMATION" +0x20 32 "$ATTRIBUTE_LIST" +0x30 48 "$FILE_NAME" +0x40 64 "$OBJECT_ID" +0x50 80 "$SECURITY_DESCRIPTOR" +0x60 96 "$VOLUME_NAME" +0x70 112 "$VOLUME_INFORMATION" +0x80 128 "$DATA" +0x90 144 "$INDEX_ROOT" +0xA0 160 "$INDEX_ALLOCATION" +0xB0 176 "$BITMAP" +0xC0 192 "$REPARSE_POINT" +0xD0 208 "$EA_INFORMATION" +0xE0 224 "$EA" +0xF0 240 "$PROPERTY_SET" +0x100 256 "$LOGGED_UTILITY_STREAM" +.TE +.sp +.TP +\fBattr-name\fR +Define the name of the particular attribute type to be truncated +(advanced use only). +.sp +.TP +\fBnew-length\fR +Specify the target size of the file. +It will be rounded up to a multiple of the cluster size. +A suffix of K, M, G, T, P or E may be appended to +mean a multiplicative factor of a power of 1000. Similarly a suffix of +Ki, Mi, Gi, Ti, Pi or Ei may be appended to mean a multiplicative factor +of a power of 1024. +.SH EXAMPLES +Resize to 100MB the file database.db located in the Data directory +which is at the root of an NTFS file system. +.RS +.sp +.B ntfstruncate /dev/sda1 Data/database.db 100M +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfstruncate . +If you find a bug, please send an email +describing the problem to the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfstruncate +was written by Anton Altaparmakov. +.SH AVAILABILITY +.B ntfstruncate +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfs-3g (8), +.BR ntfsfallocate (8), +.BR ntfsprogs (8). diff --git a/ntfsprogs/ntfstruncate.c b/ntfsprogs/ntfstruncate.c new file mode 100755 index 0000000000000000000000000000000000000000..f561690fc206168c1d3d47b31db61ad0b0dfafd0 --- /dev/null +++ b/ntfsprogs/ntfstruncate.c @@ -0,0 +1,810 @@ +/** + * ntfstruncate - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Anton Altaparmakov + * + * This utility will truncate a specified attribute belonging to a + * specified inode, i.e. file or directory, to a specified length. + * + * 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 (in the main directory of the Linux-NTFS source + * in the file COPYING); if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif +#ifdef HAVE_STDIO_H +# include <stdio.h> +#endif +#ifdef HAVE_STDARG_H +# include <stdarg.h> +#endif +#ifdef HAVE_STRING_H +# include <string.h> +#endif +#ifdef HAVE_ERRNO_H +# include <errno.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_GETOPT_H +# include <getopt.h> +#else + extern char *optarg; + extern int optind; +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifndef LLONG_MAX +# define LLONG_MAX 9223372036854775807LL +#endif + +#include "types.h" +#include "attrib.h" +#include "inode.h" +#include "layout.h" +#include "volume.h" +#include "utils.h" +#include "attrdef.h" +/* #include "version.h" */ +#include "logging.h" + +const char *EXEC_NAME = "ntfstruncate"; + +/* Need these global so ntfstruncate_exit can access them. */ +BOOL success = FALSE; + +char *dev_name; +s64 inode; +u32 attr_type; +ntfschar *attr_name = NULL; +u32 attr_name_len; +s64 new_len; + +ntfs_volume *vol; +ntfs_inode *ni; +ntfs_attr *na = NULL; + +ATTR_DEF *attr_defs; + +struct { + /* -h, print usage and exit. */ + int no_action; /* -n, do not write to device, only display + what would be done. */ + int quiet; /* -q, quiet execution. */ + int verbose; /* -v, verbose execution, given twice, really + verbose execution (debug mode). */ + int force; /* -f, force truncation. */ + /* -V, print version and exit. */ +} opts; + +/** + * err_exit - error output and terminate; ignores quiet (-q) + */ +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +static void err_exit(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "ERROR: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "Aborting...\n"); + exit(1); +} + +/** + * copyright - print copyright statements + */ +static void copyright(void) +{ + fprintf(stderr, "Copyright (c) 2002-2005 Anton Altaparmakov\n" + "Copyright (c) 2003 Richard Russon\n" + "Truncate a specified attribute of a specified " + "inode.\n"); +} + +/** + * license - print license statement + */ +static void license(void) +{ + fprintf(stderr, "%s", ntfs_gpl); +} + +/** + * usage - print a list of the parameters to the program + */ +__attribute__((noreturn)) +static void usage(int ret) +{ + copyright(); + fprintf(stderr, "Usage: %s [options] device inode [attr-type " + "[attr-name]] new-length\n" + " If attr-type is not specified, 0x80 (i.e. $DATA) " + "is assumed.\n" + " If attr-name is not specified, an unnamed " + "attribute is assumed.\n" + " -n Do not write to disk\n" + " -f Force execution despite errors\n" + " -q Quiet execution\n" + " -v Verbose execution\n" + " -vv Very verbose execution\n" + " -V Display version information\n" + " -l Display licensing information\n" + " -h Display this help\n", EXEC_NAME); + fprintf(stderr, "%s%s", ntfs_bugs, ntfs_home); + exit(ret); +} + +/** + * parse_options + */ +static void parse_options(int argc, char *argv[]) +{ + long long ll; + char *s, *s2; + int c; + + if (argc && *argv) + EXEC_NAME = *argv; + fprintf(stderr, "%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); + while ((c = getopt(argc, argv, "fh?nqvVl")) != EOF) + switch (c) { + case 'f': + opts.force = 1; + break; + case 'n': + opts.no_action = 1; + break; + case 'q': + opts.quiet = 1; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + /* Version number already printed, so just exit. */ + exit(0); + case 'l': + copyright(); + license(); + exit(0); + case 'h': + usage(0); + case '?': + default: + usage(1); + } + if (optind == argc) + usage(1); + + if (opts.verbose > 1) + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | + NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_QUIET); + + /* Get the device. */ + dev_name = argv[optind++]; + ntfs_log_verbose("device name = %s\n", dev_name); + + if (optind == argc) + usage(1); + + /* Get the inode. */ + ll = strtoll(argv[optind++], &s, 0); + if (*s || !ll || (ll >= LLONG_MAX && errno == ERANGE)) + err_exit("Invalid inode number: %s\n", argv[optind - 1]); + inode = ll; + ntfs_log_verbose("inode = %lli\n", (long long)inode); + + if (optind == argc) + usage(1); + + /* Get the attribute type, if specified. */ + s = argv[optind++]; + if (optind == argc) { + attr_type = AT_DATA; + attr_name = AT_UNNAMED; + attr_name_len = 0; + } else { + unsigned long ul; + + ul = strtoul(s, &s2, 0); + if (*s2 || !ul || (ul >= ULONG_MAX && errno == ERANGE)) + err_exit("Invalid attribute type %s: %s\n", s, + strerror(errno)); + attr_type = ul; + + /* Get the attribute name, if specified. */ + s = argv[optind++]; + if (optind != argc) { + /* Convert the string to little endian Unicode. */ + attr_name_len = ntfs_mbstoucs(s, &attr_name); + if ((int)attr_name_len < 0) + err_exit("Invalid attribute name \"%s\": %s\n", + s, strerror(errno)); + + /* Keep hold of the original string. */ + s2 = s; + + s = argv[optind++]; + if (optind != argc) + usage(1); + } else { + attr_name = AT_UNNAMED; + attr_name_len = 0; + } + } + ntfs_log_verbose("attribute type = 0x%x\n", (unsigned int)attr_type); + if (attr_name == AT_UNNAMED) + ntfs_log_verbose("attribute name = \"\" (UNNAMED)\n"); + else + ntfs_log_verbose("attribute name = \"%s\" (length %u Unicode " + "characters)\n", s2, + (unsigned int)attr_name_len); + + /* Get the new length. */ + ll = strtoll(s, &s2, 0); + if (*s2 || ll < 0 || (ll >= LLONG_MAX && errno == ERANGE)) + err_exit("Invalid new length: %s\n", s); + new_len = ll; + ntfs_log_verbose("new length = %lli\n", (long long)new_len); +} + +/** + * ucstos - convert unicode-character string to ASCII + * @dest: points to buffer to receive the converted string + * @src: points to string to convert + * @maxlen: size of @dest buffer in bytes + * + * Return the number of characters written to @dest, not including the + * terminating null byte. If a unicode character was encountered which could + * not be converted -1 is returned. + */ +static int ucstos(char *dest, const ntfschar *src, int maxlen) +{ + ntfschar u; + int i; + + /* Need one byte for null terminator. */ + maxlen--; + for (i = 0; i < maxlen; i++) { + u = le16_to_cpu(src[i]); + if (!u) + break; + if (u & 0xff00) + return -1; + dest[i] = u & 0xff; + } + dest[i] = 0; + return i; +} + +/** + * dump_resident_attr_val + */ +static void dump_resident_attr_val(ATTR_TYPES type, char *val, u32 val_len) +{ + const char *don_t_know = "Don't know what to do with this attribute " + "type yet."; + const char *skip = "Skipping display of $%s attribute value.\n"; + const char *todo = "This is still work in progress."; + char *buf; + int i, j; + u32 u; + + switch (type) { + case AT_STANDARD_INFORMATION: + // TODO + printf("%s\n", todo); + return; + case AT_ATTRIBUTE_LIST: + // TODO + printf("%s\n", todo); + return; + case AT_FILE_NAME: + // TODO + printf("%s\n", todo); + return; + case AT_OBJECT_ID: + // TODO + printf("%s\n", todo); + return; + case AT_SECURITY_DESCRIPTOR: + // TODO + printf("%s\n", todo); + return; + case AT_VOLUME_NAME: + printf("Volume name length = %u\n", (unsigned int)val_len); + if (val_len) { + buf = calloc(1, val_len); + if (!buf) + err_exit("Failed to allocate internal buffer: " + "%s\n", strerror(errno)); + i = ucstos(buf, (ntfschar*)val, val_len); + if (i == -1) + printf("Volume name contains non-displayable " + "Unicode characters.\n"); + printf("Volume name = %s\n", buf); + free(buf); + } + return; + case AT_VOLUME_INFORMATION: +#define VOL_INF(x) ((VOLUME_INFORMATION *)(x)) + printf("NTFS version %i.%i\n", VOL_INF(val)->major_ver, + VOL_INF(val)->minor_ver); + i = VOL_INF(val)->flags; +#undef VOL_INF + printf("Volume flags = 0x%x: ", i); + if (!i) { + printf("NONE\n"); + return; + } + j = 0; + if (i & VOLUME_MODIFIED_BY_CHKDSK) { + j = 1; + printf("VOLUME_MODIFIED_BY_CHKDSK"); + } + if (i & VOLUME_REPAIR_OBJECT_ID) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_REPAIR_OBJECT_ID"); + } + if (i & VOLUME_DELETE_USN_UNDERWAY) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_DELETE_USN_UNDERWAY"); + } + if (i & VOLUME_MOUNTED_ON_NT4) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_MOUNTED_ON_NT4"); + } + if (i & VOLUME_UPGRADE_ON_MOUNT) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_UPGRADE_ON_MOUNT"); + } + if (i & VOLUME_RESIZE_LOG_FILE) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_RESIZE_LOG_FILE"); + } + if (i & VOLUME_IS_DIRTY) { + if (j) + printf(" | "); + else + j = 0; + printf("VOLUME_IS_DIRTY"); + } + printf("\n"); + return; + case AT_DATA: + printf(skip, "DATA"); + return; + case AT_INDEX_ROOT: + // TODO + printf("%s\n", todo); + return; + case AT_INDEX_ALLOCATION: + // TODO + printf("%s\n", todo); + return; + case AT_BITMAP: + printf(skip, "BITMAP"); + return; + case AT_REPARSE_POINT: + // TODO + printf("%s\n", todo); + return; + case AT_EA_INFORMATION: + // TODO + printf("%s\n", don_t_know); + return; + case AT_EA: + // TODO + printf("%s\n", don_t_know); + return; + case AT_LOGGED_UTILITY_STREAM: + // TODO + printf("%s\n", don_t_know); + return; + default: + u = le32_to_cpu(type); + printf("Cannot display unknown %s defined attribute type 0x%x" + ".\n", u >= + le32_to_cpu(AT_FIRST_USER_DEFINED_ATTRIBUTE) ? + "user" : "system", (unsigned int)u); + } +} + +/** + * dump_resident_attr + */ +static void dump_resident_attr(ATTR_RECORD *a) +{ + int i; + + i = le32_to_cpu(a->value_length); + printf("Attribute value length = %u (0x%x)\n", i, i); + i = le16_to_cpu(a->value_offset); + printf("Attribute value offset = %u (0x%x)\n", i, i); + i = a->resident_flags; + printf("Resident flags = 0x%x: ", i); + if (!i) + printf("NONE\n"); + else if (i & ~RESIDENT_ATTR_IS_INDEXED) + printf("UNKNOWN FLAG(S)\n"); + else + printf("RESIDENT_ATTR_IS_INDEXED\n"); + dump_resident_attr_val(a->type, (char*)a + le16_to_cpu(a->value_offset), + le32_to_cpu(a->value_length)); +} + +/** + * dump_mapping_pairs_array + */ +static void dump_mapping_pairs_array(char *b __attribute__((unused)), + unsigned int max_len __attribute__((unused))) +{ + // TODO + return; +} + +/** + * dump_non_resident_attr + */ +static void dump_non_resident_attr(ATTR_RECORD *a) +{ + s64 l; + int i; + + l = sle64_to_cpu(a->lowest_vcn); + printf("Lowest VCN = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->highest_vcn); + printf("Highest VCN = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + printf("Mapping pairs array offset = 0x%x\n", + le16_to_cpu(a->mapping_pairs_offset)); + printf("Compression unit = 0x%x: %sCOMPRESSED\n", a->compression_unit, + a->compression_unit ? "" : "NOT "); + if (sle64_to_cpu(a->lowest_vcn)) + printf("Attribute is not the first extent. The following " + "sizes are meaningless:\n"); + l = sle64_to_cpu(a->allocated_size); + printf("Allocated size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->data_size); + printf("Data size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + l = sle64_to_cpu(a->initialized_size); + printf("Initialized size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + if (a->flags & ATTR_COMPRESSION_MASK) { + l = sle64_to_cpu(a->compressed_size); + printf("Compressed size = %lli (0x%llx)\n", (long long)l, + (unsigned long long)l); + } + i = le16_to_cpu(a->mapping_pairs_offset); + dump_mapping_pairs_array((char*)a + i, le32_to_cpu(a->length) - i); +} + +/** + * dump_attr_record + */ +static void dump_attr_record(MFT_RECORD *m, ATTR_RECORD *a) +{ + unsigned int u; + char s[0x200]; + int i; + + printf("-- Beginning dump of attribute record at offset 0x%x. --\n", + (unsigned)((u8*)a - (u8*)m)); + if (a->type == AT_END) { + printf("Attribute type = 0x%x ($END)\n", + (unsigned int)le32_to_cpu(AT_END)); + u = le32_to_cpu(a->length); + printf("Length of resident part = %u (0x%x)\n", u, u); + return; + } + u = le32_to_cpu(a->type); + for (i = 0; attr_defs[i].type; i++) + if (le32_to_cpu(attr_defs[i].type) >= u) + break; + if (attr_defs[i].type) { +// printf("type = 0x%x\n", le32_to_cpu(attr_defs[i].type)); +// { char *p = (char*)attr_defs[i].name; +// printf("name = %c%c%c%c%c\n", *p, p[1], p[2], p[3], p[4]); +// } + if (ucstos(s, attr_defs[i].name, sizeof(s)) == -1) { + ntfs_log_error("Could not convert Unicode string to single " + "byte string in current locale.\n"); + strncpy(s, "Error converting Unicode string", + sizeof(s)); + } + } else + strncpy(s, "UNKNOWN_TYPE", sizeof(s)); + printf("Attribute type = 0x%x (%s)\n", u, s); + u = le32_to_cpu(a->length); + printf("Length of resident part = %u (0x%x)\n", u, u); + printf("Attribute is %sresident\n", a->non_resident ? "non-" : ""); + printf("Name length = %u unicode characters\n", a->name_length); + printf("Name offset = %u (0x%x)\n", cpu_to_le16(a->name_offset), + cpu_to_le16(a->name_offset)); + u = a->flags; + if (a->name_length) { + if (ucstos(s, (ntfschar*)((char*)a + + cpu_to_le16(a->name_offset)), + min((int)sizeof(s), + a->name_length + 1)) == -1) { + ntfs_log_error("Could not convert Unicode string to single " + "byte string in current locale.\n"); + strncpy(s, "Error converting Unicode string", + sizeof(s)); + + } + printf("Name = %s\n", s); + } + printf("Attribute flags = 0x%x: ", le16_to_cpu(u)); + if (!u) + printf("NONE"); + else { + int first = TRUE; + if (u & ATTR_COMPRESSION_MASK) { + if (u & ATTR_IS_COMPRESSED) { + printf("ATTR_IS_COMPRESSED"); + first = FALSE; + } + if ((u & ATTR_COMPRESSION_MASK) & ~ATTR_IS_COMPRESSED) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_UNKNOWN_COMPRESSION"); + } + } + if (u & ATTR_IS_ENCRYPTED) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_IS_ENCRYPTED"); + } + if (u & ATTR_IS_SPARSE) { + if (!first) + printf(" | "); + else + first = FALSE; + printf("ATTR_IS_SPARSE"); + } + } + printf("\n"); + printf("Attribute instance = %u\n", le16_to_cpu(a->instance)); + if (a->non_resident) { + dump_non_resident_attr(a); + } else { + dump_resident_attr(a); + } +} + +/** + * dump_mft_record + */ +static void dump_mft_record(MFT_RECORD *m) +{ + ATTR_RECORD *a; + unsigned int u; + MFT_REF r; + + printf("-- Beginning dump of mft record. --\n"); + u = le32_to_cpu(m->magic); + printf("Mft record signature (magic) = %c%c%c%c\n", u & 0xff, + u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff); + u = le16_to_cpu(m->usa_ofs); + printf("Update sequence array offset = %u (0x%x)\n", u, u); + printf("Update sequence array size = %u\n", le16_to_cpu(m->usa_count)); + printf("$LogFile sequence number (lsn) = %llu\n", + (unsigned long long)le64_to_cpu(m->lsn)); + printf("Sequence number = %u\n", le16_to_cpu(m->sequence_number)); + printf("Reference (hard link) count = %u\n", + le16_to_cpu(m->link_count)); + u = le16_to_cpu(m->attrs_offset); + printf("First attribute offset = %u (0x%x)\n", u, u); + printf("Flags = %u: ", le16_to_cpu(m->flags)); + if (m->flags & MFT_RECORD_IN_USE) + printf("MFT_RECORD_IN_USE"); + else + printf("MFT_RECORD_NOT_IN_USE"); + if (m->flags & MFT_RECORD_IS_DIRECTORY) + printf(" | MFT_RECORD_IS_DIRECTORY"); + printf("\n"); + u = le32_to_cpu(m->bytes_in_use); + printf("Bytes in use = %u (0x%x)\n", u, u); + u = le32_to_cpu(m->bytes_allocated); + printf("Bytes allocated = %u (0x%x)\n", u, u); + r = le64_to_cpu(m->base_mft_record); + printf("Base mft record reference:\n\tMft record number = %llu\n\t" + "Sequence number = %u\n", + (unsigned long long)MREF(r), MSEQNO(r)); + printf("Next attribute instance = %u\n", + le16_to_cpu(m->next_attr_instance)); + a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); + printf("-- Beginning dump of attributes within mft record. --\n"); + while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) { + if (a->type == cpu_to_le32(attr_type)) + dump_attr_record(m, a); + if (a->type == AT_END) + break; + a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); + }; + printf("-- End of attributes. --\n"); +} + +/** + * ntfstruncate_exit + */ +static void ntfstruncate_exit(void) +{ + int err; + + if (success) + return; + /* Close the attribute. */ + if (na) + ntfs_attr_close(na); + /* Close the inode. */ + if (ni && ntfs_inode_close(ni)) { + ntfs_log_perror("Warning: Failed to close inode %lli", + (long long)inode); + } + /* Unmount the volume. */ + err = ntfs_umount(vol, 0); + if (err == -1) + ntfs_log_perror("Warning: Could not umount %s", dev_name); + /* Free the attribute name if it exists. */ + ntfs_ucsfree(attr_name); +} + +/** + * main + */ +int main(int argc, char **argv) +{ + unsigned long mnt_flags, ul; + int err; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + /* Initialize opts to zero / required values. */ + memset(&opts, 0, sizeof(opts)); + + /* + * Setup a default $AttrDef. FIXME: Should be reading this from the + * volume itself, at ntfs_mount() time. + */ + attr_defs = (ATTR_DEF*)&attrdef_ntfs3x_array; + + /* Parse command line options. */ + parse_options(argc, argv); + + utils_set_locale(); + + /* Make sure the file system is not mounted. */ + if (ntfs_check_if_mounted(dev_name, &mnt_flags)) + ntfs_log_perror("Failed to determine whether %s is mounted", + dev_name); + else if (mnt_flags & NTFS_MF_MOUNTED) { + ntfs_log_error("%s is mounted.\n", dev_name); + if (!opts.force) + err_exit("Refusing to run!\n"); + fprintf(stderr, "ntfstruncate forced anyway. Hope /etc/mtab " + "is incorrect.\n"); + } + + /* Mount the device. */ + if (opts.no_action) { + ntfs_log_quiet("Running in READ-ONLY mode!\n"); + ul = NTFS_MNT_RDONLY; + } else + ul = 0; + vol = ntfs_mount(dev_name, ul); + if (!vol) + err_exit("Failed to mount %s: %s\n", dev_name, strerror(errno)); + + /* Register our exit function which will unlock and close the device. */ + err = atexit(&ntfstruncate_exit); + if (err == -1) { + ntfs_log_error("Could not set up exit() function because atexit() " + "failed: %s Aborting...\n", strerror(errno)); + ntfstruncate_exit(); + exit(1); + } + + /* Open the specified inode. */ + ni = ntfs_inode_open(vol, inode); + if (!ni) + err_exit("Failed to open inode %lli: %s\n", (long long)inode, + strerror(errno)); + + /* Open the specified attribute. */ + na = ntfs_attr_open(ni, attr_type, attr_name, attr_name_len); + if (!na) + err_exit("Failed to open attribute 0x%x: %s\n", + (unsigned int)attr_type, strerror(errno)); + + if (!opts.quiet && opts.verbose > 1) { + ntfs_log_verbose("Dumping mft record before calling " + "ntfs_attr_truncate():\n"); + dump_mft_record(ni->mrec); + } + + /* Truncate the attribute. */ + err = ntfs_attr_truncate(na, new_len); + if (err) + err_exit("Failed to truncate attribute 0x%x: %s\n", + (unsigned int)attr_type, strerror(errno)); + + if (!opts.quiet && opts.verbose > 1) { + ntfs_log_verbose("Dumping mft record after calling " + "ntfs_attr_truncate():\n"); + dump_mft_record(ni->mrec); + } + + /* Close the attribute. */ + ntfs_attr_close(na); + na = NULL; + + /* Close the inode. */ + err = ntfs_inode_close(ni); + if (err) + err_exit("Failed to close inode %lli: %s\n", (long long)inode, + strerror(errno)); + + /* Unmount the volume. */ + err = ntfs_umount(vol, 0); + if (err == -1) + ntfs_log_perror("Warning: Failed to umount %s", dev_name); + + /* Free the attribute name if it exists. */ + ntfs_ucsfree(attr_name); + + /* Finally, disable our ntfstruncate_exit() handler. */ + success = TRUE; + + ntfs_log_quiet("ntfstruncate completed successfully. Have a nice day.\n"); + return 0; +} diff --git a/ntfsprogs/ntfsundelete.8 b/ntfsprogs/ntfsundelete.8 new file mode 100755 index 0000000000000000000000000000000000000000..91df57e87c27fcee7ecc4c2cbd5355581b42b9ce --- /dev/null +++ b/ntfsprogs/ntfsundelete.8 @@ -0,0 +1,324 @@ +.\" Copyright (c) 2002\-2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSUNDELETE 8 "November 2005" "ntfs-3g 2015.3.14" +.SH NAME +ntfsundelete \- recover a deleted file from an NTFS volume. +.SH SYNOPSIS +.B ntfsundelete +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfsundelete +has three modes of operation: +.IR scan , +.I undelete +and +.IR copy . +.SS Scan +.PP +The default mode, +.I scan +simply reads an NTFS Volume and looks for files that have been deleted. Then it +will print a list giving the inode number, name and size. +.SS Undelete +.PP +The +.I undelete +mode takes the files either matching the regular expression (option \-m) +or specified by the inode\-expressions and recovers as much of the data +as possible. It saves the result to another location. Partly for +safety, but mostly because NTFS write support isn't finished. +.SS Copy +.PP +This is a wizard's option. It will save a portion of the MFT to a file. This +probably only be useful when debugging +.I ntfsundelete +.SS Notes +.B ntfsundelete +only ever +.B reads +from the NTFS Volume. +.B ntfsundelete +will never change the volume. +.SH CAVEATS +.SS Miracles +.B ntfsundelete +cannot perform the impossible. +.PP +When a file is deleted the MFT Record is marked as not in use and the bitmap +representing the disk usage is updated. If the power isn't turned off +immediately, the free space, where the file used to live, may become +overwritten. Worse, the MFT Record may be reused for another file. If this +happens it is impossible to tell where the file was on disk. +.PP +Even if all the clusters of a file are not in use, there is no guarantee that +they haven't been overwritten by some short\-lived file. +.SS Locale +In NTFS all the filenames are stored as Unicode. They will be converted into +the current locale for display by +.BR ntfsundelete . +The utility has successfully displayed some Chinese pictogram filenames and then +correctly recovered them. +.SS Extended MFT Records +In rare circumstances, a single MFT Record will not be large enough to hold the +metadata describing a file (a file would have to be in hundreds of fragments +for this to happen). In these cases one MFT record may hold the filename, but +another will hold the information about the data. +.B ntfsundelete +will not try and piece together such records. It will simply show unnamed files +with data. +.SS Compressed and Encrypted Files +.B ntfsundelete +cannot recover compressed or encrypted files. When scanning for them, it will +display as being 0% recoverable. +.SS The Recovered File's Size and Date +To recover a file +.B ntfsundelete +has to read the file's metadata. Unfortunately, this isn't always intact. +When a file is deleted, the metadata can be left in an inconsistent state. e.g. +the file size may be zero; the dates of the file may be set to the time it was +deleted, or random. +.br +To be safe +.B ntfsundelete +will pick the largest file size it finds and write that to disk. It will also +try and set the file's date to the last modified date. This date may be the +correct last modified date, or something unexpected. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsundelete +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-b\fR, \fB\-\-byte\fR NUM +If any clusters of the file cannot be recovered, the missing parts will be +filled with this byte. The default is zeros. +.TP +\fB\-C\fR, \fB\-\-case\fR +When scanning an NTFS volume, any filename matching (using the +.B \-\-match +option) is case\-insensitive. This option makes the matching case\-sensitive. +.TP +\fB\-c\fR, \fB\-\-copy\fR RANGE +This wizard's option will write a block of MFT FILE records to a file. The +default file is +.I mft +which will be created in the current directory. This option can be combined +with the +.B \-\-output +and +.B \-\-destination +options. +.TP +\fB\-d\fR, \fB\-\-destination\fR DIR +This option controls where to put the output file of the +.B \-\-undelete +and +.B \-\-copy +options. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not overwriting an existing +file. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-i\fR, \fB\-\-inodes\fR RANGE +Recover the files with these inode numbers. +.I RANGE +can be a single inode number, several numbers separated by commas "," or a +range separated by a dash "\-". +.TP +\fB\-m\fR, \fB\-\-match\fR PATTERN +Filter the output by only looking for matching filenames. The pattern can +include the wildcards '?', match exactly one character or '*', match zero or +more characters. By default the matching is case\-insensitive. To make the +search case sensitive, use the +.B \-\-case +option. +.TP +\fB\-O\fR, \fB\-\-optimistic\fR +Recover parts of the file even if they are currently marked as in use. +.TP +\fB\-o\fR, \fB\-\-output\fR FILE +Use this option to set name of output file that +.B \-\-undelete +or +.B \-\-copy +will create. +.TP +\fB\-P\fR, \fB\-\-parent\fR +Display the parent directory of a deleted file. +.TP +\fB\-p\fR, \fB\-\-percentage\fR NUM +Filter the output of the +.B \-\-scan +option, by only matching files with a certain amount of recoverable content. +.B Please read the caveats section for more details. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Reduce the amount of output to a minimum. Naturally, it doesn't make sense to +combine this option with +.BR \-\-scan . +.TP +\fB\-s\fR, \fB\-\-scan\fR +Search through an NTFS volume and print a list of files that could be recovered. +This is the default action of +.BR ntfsundelete . +This list can be filtered by filename, size, percentage recoverable or last +modification time, using the +.BR \-\-match , +.BR \-\-size , +.B \-\-percent +and +.B \-\-time +options, respectively. +.sp +The output of scan will be: +.sp +.nf +Inode Flags %age Date Time Size Filename + 6038 FN.. 93% 2002\-07\-17 13:42 26629 thesis.doc +.fi +.TS +box; +lB lB +l l. +Flag Description +F/D File/Directory +N/R (Non\-)Resident data stream +C/E Compressed/Encrypted data stream +! Missing attributes +.TE +.sp +.sp +The percentage field shows how much of the file can potentially be recovered. +.TP +\fB\-S\fR, \fB\-\-size\fR RANGE +Filter the output of the +.B \-\-scan +option, by looking for a particular range of file sizes. The range may be +specified as two numbers separated by a '\-'. The sizes may be abbreviated +using the suffixes k, m, g, t, for kilobytes, megabytes, gigabytes and terabytes +respectively. +.TP +\fB\-t\fR, \fB\-\-time\fR SINCE +Filter the output of the +.B \-\-scan +option. Only match files that have been altered since this time. The time must +be given as number using a suffix of d, w, m, y for days, weeks, months or years +ago. +.TP +\fB\-T\fR, \fB\-\-truncate\fR +If +.B ntfsundelete +is confident about the size of a deleted file, then it will restore the file to +exactly that size. The default behaviour is to round up the size to the nearest +cluster (which will be a multiple of 512 bytes). +.TP +\fB\-u\fR, \fB\-\-undelete\fR +Select +.B undelete +mode. You can specify the files to be recovered using by using +.B \-\-match +or +.B \-\-inodes +options. This option can be combined with +.BR \-\-output , +.BR \-\-destination , +and +.BR \-\-byte . +.sp +When the file is recovered it will be given its original name, unless the +.B \-\-output +option is used. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfsundelete +prints. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license for +.BR ntfsundelete . +.SH EXAMPLES +Look for deleted files on /dev/hda1. +.RS +.sp +.B ntfsundelete /dev/hda1 +.sp +.RE +Look for deleted documents on /dev/hda1. +.RS +.sp +.B ntfsundelete /dev/hda1 \-s \-m '*.doc' +.sp +.RE +Look for deleted files between 5000 and 6000000 bytes, with at least 90% of the +data recoverable, on /dev/hda1. +.RS +.sp +.B ntfsundelete /dev/hda1 \-S 5k\-6m \-p 90 +.sp +.RE +Look for deleted files altered in the last two days +.RS +.sp +.B ntfsundelete /dev/hda1 \-t 2d +.sp +.RE +Undelete inodes 2, 5 and 100 to 131 of device /dev/sda1 +.RS +.sp +.B ntfsundelete /dev/sda1 \-u \-i 2,5,100\-131 +.sp +.RE +Undelete inode number 3689, call the file 'work.doc', set it to recovered +size and put it in the user's home directory. +.RS +.sp +.B ntfsundelete /dev/hda1 \-u \-T \-i 3689 \-o work.doc \-d ~ +.sp +.RE +Save MFT Records 3689 to 3690 to a file 'debug' +.RS +.sp +.B ntfsundelete /dev/hda1 \-c 3689\-3690 \-o debug +.sp +.RE +.SH BUGS +There are some small limitations to +.BR ntfsundelete , +but currently no known bugs. If you find a bug please send an email describing +the problem to the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfsundelete +was written by Richard Russon and Holger Ohmacht, with contributions from Anton +Altaparmakov. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsundelete +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsinfo (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsundelete.8.in b/ntfsprogs/ntfsundelete.8.in new file mode 100755 index 0000000000000000000000000000000000000000..f80a4d75fec6554722ab98632360c6654d18c6b7 --- /dev/null +++ b/ntfsprogs/ntfsundelete.8.in @@ -0,0 +1,324 @@ +.\" Copyright (c) 2002\-2005 Richard Russon. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSUNDELETE 8 "November 2005" "ntfs-3g @VERSION@" +.SH NAME +ntfsundelete \- recover a deleted file from an NTFS volume. +.SH SYNOPSIS +.B ntfsundelete +[\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfsundelete +has three modes of operation: +.IR scan , +.I undelete +and +.IR copy . +.SS Scan +.PP +The default mode, +.I scan +simply reads an NTFS Volume and looks for files that have been deleted. Then it +will print a list giving the inode number, name and size. +.SS Undelete +.PP +The +.I undelete +mode takes the files either matching the regular expression (option \-m) +or specified by the inode\-expressions and recovers as much of the data +as possible. It saves the result to another location. Partly for +safety, but mostly because NTFS write support isn't finished. +.SS Copy +.PP +This is a wizard's option. It will save a portion of the MFT to a file. This +probably only be useful when debugging +.I ntfsundelete +.SS Notes +.B ntfsundelete +only ever +.B reads +from the NTFS Volume. +.B ntfsundelete +will never change the volume. +.SH CAVEATS +.SS Miracles +.B ntfsundelete +cannot perform the impossible. +.PP +When a file is deleted the MFT Record is marked as not in use and the bitmap +representing the disk usage is updated. If the power isn't turned off +immediately, the free space, where the file used to live, may become +overwritten. Worse, the MFT Record may be reused for another file. If this +happens it is impossible to tell where the file was on disk. +.PP +Even if all the clusters of a file are not in use, there is no guarantee that +they haven't been overwritten by some short\-lived file. +.SS Locale +In NTFS all the filenames are stored as Unicode. They will be converted into +the current locale for display by +.BR ntfsundelete . +The utility has successfully displayed some Chinese pictogram filenames and then +correctly recovered them. +.SS Extended MFT Records +In rare circumstances, a single MFT Record will not be large enough to hold the +metadata describing a file (a file would have to be in hundreds of fragments +for this to happen). In these cases one MFT record may hold the filename, but +another will hold the information about the data. +.B ntfsundelete +will not try and piece together such records. It will simply show unnamed files +with data. +.SS Compressed and Encrypted Files +.B ntfsundelete +cannot recover compressed or encrypted files. When scanning for them, it will +display as being 0% recoverable. +.SS The Recovered File's Size and Date +To recover a file +.B ntfsundelete +has to read the file's metadata. Unfortunately, this isn't always intact. +When a file is deleted, the metadata can be left in an inconsistent state. e.g. +the file size may be zero; the dates of the file may be set to the time it was +deleted, or random. +.br +To be safe +.B ntfsundelete +will pick the largest file size it finds and write that to disk. It will also +try and set the file's date to the last modified date. This date may be the +correct last modified date, or something unexpected. +.SH OPTIONS +Below is a summary of all the options that +.B ntfsundelete +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-b\fR, \fB\-\-byte\fR NUM +If any clusters of the file cannot be recovered, the missing parts will be +filled with this byte. The default is zeros. +.TP +\fB\-C\fR, \fB\-\-case\fR +When scanning an NTFS volume, any filename matching (using the +.B \-\-match +option) is case\-insensitive. This option makes the matching case\-sensitive. +.TP +\fB\-c\fR, \fB\-\-copy\fR RANGE +This wizard's option will write a block of MFT FILE records to a file. The +default file is +.I mft +which will be created in the current directory. This option can be combined +with the +.B \-\-output +and +.B \-\-destination +options. +.TP +\fB\-d\fR, \fB\-\-destination\fR DIR +This option controls where to put the output file of the +.B \-\-undelete +and +.B \-\-copy +options. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not overwriting an existing +file. Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-i\fR, \fB\-\-inodes\fR RANGE +Recover the files with these inode numbers. +.I RANGE +can be a single inode number, several numbers separated by commas "," or a +range separated by a dash "\-". +.TP +\fB\-m\fR, \fB\-\-match\fR PATTERN +Filter the output by only looking for matching filenames. The pattern can +include the wildcards '?', match exactly one character or '*', match zero or +more characters. By default the matching is case\-insensitive. To make the +search case sensitive, use the +.B \-\-case +option. +.TP +\fB\-O\fR, \fB\-\-optimistic\fR +Recover parts of the file even if they are currently marked as in use. +.TP +\fB\-o\fR, \fB\-\-output\fR FILE +Use this option to set name of output file that +.B \-\-undelete +or +.B \-\-copy +will create. +.TP +\fB\-P\fR, \fB\-\-parent\fR +Display the parent directory of a deleted file. +.TP +\fB\-p\fR, \fB\-\-percentage\fR NUM +Filter the output of the +.B \-\-scan +option, by only matching files with a certain amount of recoverable content. +.B Please read the caveats section for more details. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Reduce the amount of output to a minimum. Naturally, it doesn't make sense to +combine this option with +.BR \-\-scan . +.TP +\fB\-s\fR, \fB\-\-scan\fR +Search through an NTFS volume and print a list of files that could be recovered. +This is the default action of +.BR ntfsundelete . +This list can be filtered by filename, size, percentage recoverable or last +modification time, using the +.BR \-\-match , +.BR \-\-size , +.B \-\-percent +and +.B \-\-time +options, respectively. +.sp +The output of scan will be: +.sp +.nf +Inode Flags %age Date Time Size Filename + 6038 FN.. 93% 2002\-07\-17 13:42 26629 thesis.doc +.fi +.TS +box; +lB lB +l l. +Flag Description +F/D File/Directory +N/R (Non\-)Resident data stream +C/E Compressed/Encrypted data stream +! Missing attributes +.TE +.sp +.sp +The percentage field shows how much of the file can potentially be recovered. +.TP +\fB\-S\fR, \fB\-\-size\fR RANGE +Filter the output of the +.B \-\-scan +option, by looking for a particular range of file sizes. The range may be +specified as two numbers separated by a '\-'. The sizes may be abbreviated +using the suffixes k, m, g, t, for kilobytes, megabytes, gigabytes and terabytes +respectively. +.TP +\fB\-t\fR, \fB\-\-time\fR SINCE +Filter the output of the +.B \-\-scan +option. Only match files that have been altered since this time. The time must +be given as number using a suffix of d, w, m, y for days, weeks, months or years +ago. +.TP +\fB\-T\fR, \fB\-\-truncate\fR +If +.B ntfsundelete +is confident about the size of a deleted file, then it will restore the file to +exactly that size. The default behaviour is to round up the size to the nearest +cluster (which will be a multiple of 512 bytes). +.TP +\fB\-u\fR, \fB\-\-undelete\fR +Select +.B undelete +mode. You can specify the files to be recovered using by using +.B \-\-match +or +.B \-\-inodes +options. This option can be combined with +.BR \-\-output , +.BR \-\-destination , +and +.BR \-\-byte . +.sp +When the file is recovered it will be given its original name, unless the +.B \-\-output +option is used. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Increase the amount of output that +.B ntfsundelete +prints. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license for +.BR ntfsundelete . +.SH EXAMPLES +Look for deleted files on /dev/hda1. +.RS +.sp +.B ntfsundelete /dev/hda1 +.sp +.RE +Look for deleted documents on /dev/hda1. +.RS +.sp +.B ntfsundelete /dev/hda1 \-s \-m '*.doc' +.sp +.RE +Look for deleted files between 5000 and 6000000 bytes, with at least 90% of the +data recoverable, on /dev/hda1. +.RS +.sp +.B ntfsundelete /dev/hda1 \-S 5k\-6m \-p 90 +.sp +.RE +Look for deleted files altered in the last two days +.RS +.sp +.B ntfsundelete /dev/hda1 \-t 2d +.sp +.RE +Undelete inodes 2, 5 and 100 to 131 of device /dev/sda1 +.RS +.sp +.B ntfsundelete /dev/sda1 \-u \-i 2,5,100\-131 +.sp +.RE +Undelete inode number 3689, call the file 'work.doc', set it to recovered +size and put it in the user's home directory. +.RS +.sp +.B ntfsundelete /dev/hda1 \-u \-T \-i 3689 \-o work.doc \-d ~ +.sp +.RE +Save MFT Records 3689 to 3690 to a file 'debug' +.RS +.sp +.B ntfsundelete /dev/hda1 \-c 3689\-3690 \-o debug +.sp +.RE +.SH BUGS +There are some small limitations to +.BR ntfsundelete , +but currently no known bugs. If you find a bug please send an email describing +the problem to the development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfsundelete +was written by Richard Russon and Holger Ohmacht, with contributions from Anton +Altaparmakov. +It was ported to ntfs-3g by Erik Larsson and Jean-Pierre Andre. +.SH AVAILABILITY +.B ntfsundelete +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfsinfo (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfsundelete.c b/ntfsprogs/ntfsundelete.c new file mode 100755 index 0000000000000000000000000000000000000000..7340dc5053f56e7ee7c1b37119c23cd343c9c2b3 --- /dev/null +++ b/ntfsprogs/ntfsundelete.c @@ -0,0 +1,2495 @@ +/** + * ntfsundelete - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2004-2005 Holger Ohmacht + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2007 Yura Pakhuchiy + * Copyright (c) 2013-2014 Jean-Pierre Andre + * + * This utility will recover deleted files from an NTFS volume. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_FEATURES_H +#include <features.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#ifdef HAVE_UTIME_H +#include <utime.h> +#endif +#ifdef HAVE_REGEX_H +#include <regex.h> +#endif + +#if !defined(REG_NOERROR) || (REG_NOERROR != 0) +#define REG_NOERROR 0 +#endif + +#ifndef REG_NOMATCH +#define REG_NOMATCH 1 +#endif + +#include "ntfsundelete.h" +#include "bootsect.h" +#include "mft.h" +#include "attrib.h" +#include "layout.h" +#include "inode.h" +#include "device.h" +#include "utils.h" +#include "debug.h" +#include "ntfstime.h" +/* #include "version.h" */ +#include "logging.h" +#include "misc.h" + +#ifdef HAVE_WINDOWS_H +/* + * Replacements for functions which do not exist on Windows + */ +#define ftruncate(fd, size) ntfs_win32_ftruncate(fd, size) +#endif + +static const char *EXEC_NAME = "ntfsundelete"; +static const char *MFTFILE = "mft"; +static const char *UNNAMED = "<unnamed>"; +static const char *NONE = "<none>"; +static const char *UNKNOWN = "unknown"; +static struct options opts; + +typedef struct +{ + u32 begin; + u32 end; +} range; + +static short with_regex; /* Flag Regular expression available */ +static short avoid_duplicate_printing; /* Flag No duplicate printing of file infos */ +static range *ranges; /* Array containing all Inode-Ranges for undelete */ +static long nr_entries; /* Number of range entries */ + +#ifdef HAVE_WINDOWS_H +/* + * Replacement for strftime() on Windows + * + * strftime() on Windows uses format codes different from those + * defined in C99 sect. 7.23.3.5 + * Use snprintf() instead. + */ +static int win32_strftime(char *buffer, int size, const char *format, + const struct tm *ptm) +{ + int ret; + + if (!strcmp(format, "%F %R")) + ret = snprintf(buffer, size, "%4d-%02d-%02d %02d:%02d", + ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min); + else + ret = snprintf(buffer, size, "%4d-%02d-%02d", + ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday); + return (ret); +} +#define strftime(buf, sz, fmt, ptm) win32_strftime(buf, sz, fmt, ptm) +#endif + +#ifndef HAVE_REGEX_H + +/* + * Pattern matching routing for systems with no regex. + */ + +typedef struct REGEX { + ntfschar *upcase; + u32 upcase_len; + int flags; + int pattern_len; + ntfschar pattern[1]; +} *regex_t; + +enum { REG_NOSUB = 1, REG_ICASE = 2 }; + +static BOOL patmatch(regex_t *re, const ntfschar *f, int flen, + const ntfschar *p, int plen, BOOL dot) +{ + regex_t pre; + BOOL ok; + BOOL anyextens; + int i; + unsigned int c; + + pre = *re; + if (pre->flags & REG_ICASE) { + while ((flen > 0) && (plen > 0) + && ((*f == *p) + || (*p == const_cpu_to_le16('?')) + || ((c = le16_to_cpu(*f)) < pre->upcase_len + ? pre->upcase[c] : *f) == *p)) { + flen--; + if (*f++ == const_cpu_to_le16('.')) + dot = TRUE; + plen--; + p++; + } + } else { + while ((flen > 0) && (plen > 0) + && ((*f == *p) || (*p == const_cpu_to_le16('?')))) { + flen--; + if (*f++ == const_cpu_to_le16('.')) + dot = TRUE; + plen--; + p++; + } + } + if ((flen <= 0) && (plen <= 0)) + ok = TRUE; + else { + ok = FALSE; + plen--; + if (*p++ == const_cpu_to_le16('*')) { + /* special case "*.*" requires the end or a dot */ + anyextens = FALSE; + if ((plen == 2) + && (p[0] == const_cpu_to_le16('.')) + && (p[1] == const_cpu_to_le16('*')) + && !dot) { + for (i=0; (i<flen) && !anyextens; i++) + if (f[i] == const_cpu_to_le16('.')) + anyextens = TRUE; + } + if (!plen || anyextens) + ok = TRUE; + else + while ((flen > 0) && !ok) + if (patmatch(re,f,flen,p,plen,dot)) + ok = TRUE; + else { + flen--; + f++; + } + } + } + return (ok); +} + +static int regcomp(regex_t *re, const char *pattern, int flags) +{ + regex_t pre; + ntfschar *rp; + ntfschar *p; + unsigned int c; + int lth; + int i; + + pre = (regex_t)malloc(sizeof(struct REGEX) + + strlen(pattern)*sizeof(ntfschar)); + *re = pre; + if (pre) { + pre->flags = flags; + pre->upcase_len = 0; + rp = pre->pattern; + lth = ntfs_mbstoucs(pattern, &rp); + pre->pattern_len = lth; + p = pre->pattern; + if (flags & REG_ICASE) { + for (i=0; i<lth; i++) { + c = le16_to_cpu(*p); + if (c < pre->upcase_len) + *p = pre->upcase[c]; + p++; + } + } + } + return (*re && (lth > 0) ? 0 : -1); +} + +static int regexec(regex_t *re, const ntfschar *uname, int len, + char *q __attribute__((unused)), int r __attribute__((unused))) +{ + BOOL m; + + m = patmatch(re, uname, len, (*re)->pattern, (*re)->pattern_len, FALSE); + return (m ? REG_NOERROR : REG_NOMATCH); +} + +static void regfree(regex_t *re) +{ + free(*re); +} + +#endif + +/** + * parse_inode_arg - parses the inode expression + * + * Parses the optarg after parameter -u for valid ranges + * + * Return: Number of correct inode specifications or -1 for error + */ +static int parse_inode_arg(void) +{ + int p; + u32 range_begin; + u32 range_end; + u32 range_temp; + u32 inode; + char *opt_arg_ptr; + char *opt_arg_temp; + char *opt_arg_end1; + char *opt_arg_end2; + + /* Check whether optarg is available or not */ + nr_entries = 0; + if (optarg == NULL) + return (0); /* bailout if no optarg */ + + /* init variables */ + p = strlen(optarg); + opt_arg_ptr = optarg; + opt_arg_end1 = optarg; + opt_arg_end2 = &(optarg[p]); + + /* alloc mem for range table */ + ranges = (range *) malloc((p + 1) * sizeof(range)); + if (ranges == NULL) { + ntfs_log_error("ERROR: Couldn't alloc mem for parsing inodes!\n"); + return (-1); + } + + /* loop */ + while ((opt_arg_end1 != opt_arg_end2) && (p > 0)) { + /* Try to get inode */ + inode = strtoul(opt_arg_ptr, &opt_arg_end1, 0); + p--; + + /* invalid char at begin */ + if ((opt_arg_ptr == opt_arg_end1) || (opt_arg_ptr == opt_arg_end2)) { + ntfs_log_error("ERROR: Invalid Number: %s\n", opt_arg_ptr); + return (-1); + } + + /* RANGE - Check for range */ + if (opt_arg_end1[0] == '-') { + /* get range end */ + opt_arg_temp = opt_arg_end1; + opt_arg_end1 = & (opt_arg_temp[1]); + if (opt_arg_temp >= opt_arg_end2) { + ntfs_log_error("ERROR: Missing range end!\n"); + return (-1); + } + range_begin = inode; + + /* get count */ + range_end = strtoul(opt_arg_end1, &opt_arg_temp, 0); + if (opt_arg_temp == opt_arg_end1) { + ntfs_log_error("ERROR: Invalid Number: %s\n", opt_arg_temp); + return (-1); + } + + /* check for correct values */ + if (range_begin > range_end) { + range_temp = range_end; + range_end = range_begin; + range_begin = range_temp; + } + + /* put into struct */ + ranges[nr_entries].begin = range_begin; + ranges[nr_entries].end = range_end; + nr_entries++; + + /* Last check */ + opt_arg_ptr = & (opt_arg_temp[1]); + if (opt_arg_ptr >= opt_arg_end2) + break; + } else if (opt_arg_end1[0] == ',') { + /* SINGLE VALUE, BUT CONTINUING */ + /* put inode into range list */ + ranges[nr_entries].begin = inode; + ranges[nr_entries].end = inode; + nr_entries++; + + /* Next inode */ + opt_arg_ptr = & (opt_arg_end1[1]); + if (opt_arg_ptr >= opt_arg_end2) { + ntfs_log_error("ERROR: Missing new value at end of input!\n"); + return (-1); + } + continue; + } else { /* SINGLE VALUE, END */ + ranges[nr_entries].begin = inode; + ranges[nr_entries].end = inode; + nr_entries++; + } + } + return (nr_entries); +} + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Recover deleted files from an " + "NTFS Volume.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2002-2005 Richard Russon\n" + "Copyright (c) 2004-2005 Holger Ohmacht\n" + "Copyright (c) 2005 Anton Altaparmakov\n" + "Copyright (c) 2007 Yura Pakhuchiy\n" + "Copyright (c) 2013-2014 Jean-Pierre Andre\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device\n" + " -s, --scan Scan for files (default)\n" + " -p, --percentage NUM Minimum percentage recoverable\n" + " -m, --match PATTERN Only work on files with matching names\n" + " -C, --case Case sensitive matching\n" + " -S, --size RANGE Match files of this size\n" + " -t, --time SINCE Last referenced since this time\n" + "\n" + " -u, --undelete Undelete mode\n" + " -i, --inodes RANGE Recover these inodes\n" + //" -I, --interactive Interactive mode\n" + " -o, --output FILE Save with this filename\n" + " -O, --optimistic Undelete in-use clusters as well\n" + " -d, --destination DIR Destination directory\n" + " -b, --byte NUM Fill missing parts with this byte\n" + " -T, --truncate Truncate 100%% recoverable file to exact size.\n" + " -P, --parent Show parent directory\n" + "\n" + " -c, --copy RANGE Write a range of MFT records to a file\n" + "\n" + " -f, --force Use less caution\n" + " -q, --quiet Less output\n" + " -v, --verbose More output\n" + " -V, --version Display version information\n" + " -h, --help Display this help\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * transform - Convert a shell style pattern to a regex + * @pattern: String to be converted + * @regex: Resulting regular expression is put here + * + * This will transform patterns, such as "*.doc" to true regular expressions. + * The function will also place '^' and '$' around the expression to make it + * behave as the user would expect + * + * Before After + * . \. + * * .* + * ? . + * + * Notes: + * The returned string must be freed by the caller. + * If transform fails, @regex will not be changed. + * + * Return: 1, Success, the string was transformed + * 0, An error occurred + */ +static int transform(const char *pattern, char **regex) +{ + char *result; + int length, i; +#ifdef HAVE_REGEX_H + int j; +#endif + + if (!pattern || !regex) + return 0; + + length = strlen(pattern); + if (length < 1) { + ntfs_log_error("Pattern to transform is empty\n"); + return 0; + } + + for (i = 0; pattern[i]; i++) { + if ((pattern[i] == '*') || (pattern[i] == '.')) + length++; + } + + result = malloc(length + 3); + if (!result) { + ntfs_log_error("Couldn't allocate memory in transform()\n"); + return 0; + } + +#ifdef HAVE_REGEX_H + result[0] = '^'; + + for (i = 0, j = 1; pattern[i]; i++, j++) { + if (pattern[i] == '*') { + result[j] = '.'; + j++; + result[j] = '*'; + } else if (pattern[i] == '.') { + result[j] = '\\'; + j++; + result[j] = '.'; + } else if (pattern[i] == '?') { + result[j] = '.'; + } else { + result[j] = pattern[i]; + } + } + + result[j] = '$'; + result[j+1] = 0; + ntfs_log_debug("Pattern '%s' replaced with regex '%s'.\n", pattern, + result); +#else + strcpy(result, pattern); +#endif + + *regex = result; + return 1; +} + +/** + * parse_time - Convert a time abbreviation to seconds + * @string: The string to be converted + * @since: The absolute time referred to + * + * Strings representing times will be converted into a time_t. The numbers will + * be regarded as seconds unless suffixed. + * + * Suffix Description + * [yY] Year + * [mM] Month + * [wW] Week + * [dD] Day + * [sS] Second + * + * Therefore, passing "1W" will return the time_t representing 1 week ago. + * + * Notes: + * Only the first character of the suffix is read. + * If parse_time fails, @since will not be changed + * + * Return: 1 Success + * 0 Error, the string was malformed + */ +static int parse_time(const char *value, time_t *since) +{ + long long result; + time_t now; + char *suffix = NULL; + + if (!value || !since) + return -1; + + ntfs_log_trace("Parsing time '%s' ago.\n", value); + + result = strtoll(value, &suffix, 10); + if (result < 0 || errno == ERANGE) { + ntfs_log_error("Invalid time '%s'.\n", value); + return 0; + } + + if (!suffix) { + ntfs_log_error("Internal error, strtoll didn't return a suffix.\n"); + return 0; + } + + if (strlen(suffix) > 1) { + ntfs_log_error("Invalid time suffix '%s'. Use Y, M, W, D or H.\n", suffix); + return 0; + } + + switch (suffix[0]) { + case 'y': case 'Y': result *= 12; + case 'm': case 'M': result *= 4; + case 'w': case 'W': result *= 7; + case 'd': case 'D': result *= 24; + case 'h': case 'H': result *= 3600; + case 0: + break; + + default: + ntfs_log_error("Invalid time suffix '%s'. Use Y, M, W, D or H.\n", suffix); + return 0; + } + + now = time(NULL); + + ntfs_log_debug("Time now = %lld, Time then = %lld.\n", (long long) now, + (long long) result); + *since = now - result; + return 1; +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char *argv[]) +{ + static const char *sopt = "-b:Cc:d:fh?i:m:o:OPp:sS:t:TuqvV"; + static const struct option lopt[] = { + { "byte", required_argument, NULL, 'b' }, + { "case", no_argument, NULL, 'C' }, + { "copy", required_argument, NULL, 'c' }, + { "destination", required_argument, NULL, 'd' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "inodes", required_argument, NULL, 'i' }, + //{ "interactive", no_argument, NULL, 'I' }, + { "match", required_argument, NULL, 'm' }, + { "optimistic", no_argument, NULL, 'O' }, + { "output", required_argument, NULL, 'o' }, + { "parent", no_argument, NULL, 'P' }, + { "percentage", required_argument, NULL, 'p' }, + { "quiet", no_argument, NULL, 'q' }, + { "scan", no_argument, NULL, 's' }, + { "size", required_argument, NULL, 'S' }, + { "time", required_argument, NULL, 't' }, + { "truncate", no_argument, NULL, 'T' }, + { "undelete", no_argument, NULL, 'u' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + char *end = NULL; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.mode = MODE_NONE; + opts.uinode = -1; + opts.percent = -1; + opts.fillbyte = -1; + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind-1]; + } else { + opts.device = NULL; + err++; + } + break; + case 'b': + if (opts.fillbyte == (char)-1) { + end = NULL; + opts.fillbyte = strtol(optarg, &end, 0); + if (end && *end) + err++; + } else { + err++; + } + break; + case 'C': + opts.match_case++; + break; + case 'c': + if (opts.mode == MODE_NONE) { + if (!utils_parse_range(optarg, + &opts.mft_begin, &opts.mft_end, TRUE)) + err++; + opts.mode = MODE_COPY; + } else { + opts.mode = MODE_ERROR; + } + break; + case 'd': + if (!opts.dest) + opts.dest = optarg; + else + err++; + break; + case 'f': + opts.force++; + break; + case 'h': + help++; + break; + case '?': + if (ntfs_log_parse_option (argv[optind-1])) + break; + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + err++; + break; + case 'i': + end = NULL; + /* parse inodes */ + if (parse_inode_arg() == -1) + err++; + if (end && *end) + err++; + break; + case 'm': + if (!opts.match) { + if (!transform(optarg, &opts.match)) { + err++; + } else { + /* set regex-flag on true ;) */ + with_regex= 1; + } + } else { + err++; + } + break; + case 'o': + if (!opts.output) { + opts.output = optarg; + } else { + err++; + } + break; + case 'O': + if (!opts.optimistic) { + opts.optimistic++; + } else { + err++; + } + break; + case 'P': + if (!opts.parent) { + opts.parent++; + } else { + err++; + } + break; + case 'p': + if (opts.percent == -1) { + end = NULL; + opts.percent = strtol(optarg, &end, 0); + if (end && ((*end != '%') && (*end != 0))) + err++; + } else { + err++; + } + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 's': + if (opts.mode == MODE_NONE) + opts.mode = MODE_SCAN; + else + opts.mode = MODE_ERROR; + break; + case 'S': + if ((opts.size_begin > 0) || (opts.size_end > 0) || + !utils_parse_range(optarg, &opts.size_begin, + &opts.size_end, TRUE)) { + err++; + } + break; + case 't': + if (opts.since == 0) { + if (!parse_time(optarg, &opts.since)) + err++; + } else { + err++; + } + break; + case 'T': + opts.truncate++; + break; + case 'u': + if (opts.mode == MODE_NONE) { + opts.mode = MODE_UNDELETE; + } else { + opts.mode = MODE_ERROR; + } + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + default: + if (((optopt == 'b') || (optopt == 'c') || + (optopt == 'd') || (optopt == 'm') || + (optopt == 'o') || (optopt == 'p') || + (optopt == 'S') || (optopt == 't') || + (optopt == 'u')) && (!optarg)) { + ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]); + } else { + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + } + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify exactly one device.\n"); + err++; + } + + if (opts.mode == MODE_NONE) { + opts.mode = MODE_SCAN; + } + + switch (opts.mode) { + case MODE_SCAN: + if (opts.output || opts.dest || opts.truncate || + (opts.fillbyte != (char)-1)) { + ntfs_log_error("Scan can only be used with --percent, " + "--match, --ignore-case, --size and --time.\n"); + err++; + } + if (opts.match_case && !opts.match) { + ntfs_log_error("The --case option doesn't make sense without the --match option\n"); + err++; + } + break; + + case MODE_UNDELETE: + /*if ((opts.percent != -1) || (opts.size_begin > 0) || (opts.size_end > 0)) { + ntfs_log_error("Undelete can only be used with " + "--output, --destination, --byte and --truncate.\n"); + err++; + }*/ + break; + case MODE_COPY: + if ((opts.fillbyte != (char)-1) || opts.truncate || + (opts.percent != -1) || + opts.match || opts.match_case || + (opts.size_begin > 0) || + (opts.size_end > 0)) { + ntfs_log_error("Copy can only be used with --output and --destination.\n"); + err++; + } + break; + default: + ntfs_log_error("You can only select one of Scan, Undelete or Copy.\n"); + err++; + } + + if ((opts.percent < -1) || (opts.percent > 100)) { + ntfs_log_error("Percentage value must be in the range 0 - 100.\n"); + err++; + } + + if (opts.quiet) { + if (opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the same time.\n"); + err++; + } else if (opts.mode == MODE_SCAN) { + ntfs_log_error("You may not use --quiet when scanning a volume.\n"); + err++; + } + } + + if (opts.parent && !opts.verbose) { + ntfs_log_error("To use --parent, you must also use --verbose.\n"); + err++; + } + } + + if (opts.fillbyte == (char)-1) + opts.fillbyte = 0; + + if (ver) + version(); + if (help || err) + usage(); + + /* tri-state 0 : done, 1 : error, -1 : proceed */ + return (err ? 1 : (help || ver ? 0 : -1)); +} + +/** + * free_file - Release the resources used by a file object + * @file: The unwanted file object + * + * This will free up the memory used by a file object and iterate through the + * object's children, freeing their resources too. + * + * Return: none + */ +static void free_file(struct ufile *file) +{ + struct ntfs_list_head *item, *tmp; + + if (!file) + return; + + ntfs_list_for_each_safe(item, tmp, &file->name) { + /* List of filenames */ + struct filename *f = ntfs_list_entry(item, struct filename, list); + ntfs_log_debug("freeing filename '%s'", f->name ? f->name : + NONE); + if (f->name) + free(f->name); + if (f->parent_name) { + ntfs_log_debug(" and parent filename '%s'", + f->parent_name); + free(f->parent_name); + } + ntfs_log_debug(".\n"); + free(f); + } + + ntfs_list_for_each_safe(item, tmp, &file->data) { + /* List of data streams */ + struct data *d = ntfs_list_entry(item, struct data, list); + ntfs_log_debug("Freeing data stream '%s'.\n", d->name ? + d->name : UNNAMED); + if (d->name) + free(d->name); + if (d->runlist) + free(d->runlist); + free(d); + } + + free(file->mft); + free(file); +} + +/** + * verify_parent - confirm a record is parent of a file + * @name: a filename of the file + * @rec: the mft record of the possible parent + * + * Check that @rec is the parent of the file represented by @name. + * If @rec is a directory, but it is created after @name, then we + * can't determine whether @rec is really @name's parent. + * + * Return: @rec's filename, either same name space as @name or lowest space. + * NULL if can't determine parenthood or on error. + */ +static FILE_NAME_ATTR* verify_parent(struct filename* name, MFT_RECORD* rec) +{ + ATTR_RECORD *attr30; + FILE_NAME_ATTR *filename_attr = NULL, *lowest_space_name = NULL; + ntfs_attr_search_ctx *ctx; + int found_same_space = 1; + + if (!name || !rec) + return NULL; + + if (!(rec->flags & MFT_RECORD_IS_DIRECTORY)) { + return NULL; + } + + ctx = ntfs_attr_get_search_ctx(NULL, rec); + if (!ctx) { + ntfs_log_error("ERROR: Couldn't create a search context.\n"); + return NULL; + } + + attr30 = find_attribute(AT_FILE_NAME, ctx); + if (!attr30) { + return NULL; + } + + filename_attr = (FILE_NAME_ATTR*)((char*)attr30 + le16_to_cpu(attr30->value_offset)); + /* if name is older than this dir -> can't determine */ + if (ntfs2timespec(filename_attr->creation_time).tv_sec > name->date_c) { + return NULL; + } + + if (filename_attr->file_name_type != name->name_space) { + found_same_space = 0; + lowest_space_name = filename_attr; + + while (!found_same_space && (attr30 = find_attribute(AT_FILE_NAME, ctx))) { + filename_attr = (FILE_NAME_ATTR*)((char*)attr30 + le16_to_cpu(attr30->value_offset)); + + if (filename_attr->file_name_type == name->name_space) { + found_same_space = 1; + } else { + if (filename_attr->file_name_type < lowest_space_name->file_name_type) { + lowest_space_name = filename_attr; + } + } + } + } + + ntfs_attr_put_search_ctx(ctx); + + return (found_same_space ? filename_attr : lowest_space_name); +} + +/** + * get_parent_name - Find the name of a file's parent. + * @name: the filename whose parent's name to find + */ +static void get_parent_name(struct filename* name, ntfs_volume* vol) +{ + ntfs_attr* mft_data; + MFT_RECORD* rec; + FILE_NAME_ATTR* filename_attr; + long long inode_num; + + if (!name || !vol) + return; + + rec = calloc(1, vol->mft_record_size); + if (!rec) { + ntfs_log_error("ERROR: Couldn't allocate memory in " + "get_parent_name()\n"); + return; + } + + mft_data = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (!mft_data) { + ntfs_log_perror("ERROR: Couldn't open $MFT/$DATA"); + } else { + inode_num = MREF_LE(name->parent_mref); + + if (ntfs_attr_pread(mft_data, vol->mft_record_size * inode_num, + vol->mft_record_size, rec) < 1) { + ntfs_log_error("ERROR: Couldn't read MFT Record %lld" + ".\n", inode_num); + } else if ((filename_attr = verify_parent(name, rec))) { + if (ntfs_ucstombs(filename_attr->file_name, + filename_attr->file_name_length, + &name->parent_name, 0) < 0) { + ntfs_log_debug("ERROR: Couldn't translate " + "filename to current " + "locale.\n"); + name->parent_name = NULL; + } + } + } + + if (mft_data) { + ntfs_attr_close(mft_data); + } + + if (rec) { + free(rec); + } + + return; +} + +/* + * Rescue the last deleted name of a file + * + * Under some conditions, when a name is deleted and the MFT + * record is shifted to reclaim the space, the name is still + * present beyond the end of record. + * + * For this to be possible, the data record has to be small (less + * than 80 bytes), and there must be no other attributes. + * So only the names of plain unfragmented files can be rescued. + * + * Returns NULL when the name cannot be recovered. + */ + +static struct filename *rescue_name(MFT_RECORD *mft, ntfs_attr_search_ctx *ctx) +{ + ATTR_RECORD *rec; + struct filename *name; + int off_name; + int length; + int type; + + name = (struct filename*)NULL; + ntfs_attr_reinit_search_ctx(ctx); + rec = find_attribute(AT_DATA, ctx); + if (rec) { + /* + * If the data attribute replaced the name attribute, + * the name itself is at offset 0x58 from the data attr. + * First be sure this location is within the unused part + * of the MFT record, then make extra checks. + */ + off_name = (long)rec - (long)mft + 0x58; + if ((off_name >= (int)le32_to_cpu(mft->bytes_in_use)) + && ((off_name + 4) + <= (int)le32_to_cpu(mft->bytes_allocated))) { + length = *((char*)mft + off_name); + type = *((char*)mft + off_name + 1); + /* check whether the name is fully allocated */ + if ((type <= 3) + && (length > 0) + && ((off_name + 2*length + 2) + <= (int)le32_to_cpu(mft->bytes_allocated))) { + /* create a (partial) name record */ + name = (struct filename*) + ntfs_calloc(sizeof(*name)); + if (name) { + name->uname = (ntfschar*) + ((char*)mft + off_name + 2); + name->uname_len = length; + name->name_space = type; + if (ntfs_ucstombs(name->uname, length, + &name->name, 0) < 0) { + free(name); + name = (struct filename*)NULL; + } + } + if (name && name->name) + ntfs_log_verbose("Recovered file name %s\n", + name->name); + } + } + } + return (name); +} + + + +/** + * get_filenames - Read an MFT Record's $FILENAME attributes + * @file: The file object to work with + * + * A single file may have more than one filename. This is quite common. + * Windows creates a short DOS name for each long name, e.g. LONGFI~1.XYZ, + * LongFiLeName.xyZ. + * + * The filenames that are found are put in filename objects and added to a + * linked list of filenames in the file object. For convenience, the unicode + * filename is converted into the current locale and stored in the filename + * object. + * + * One of the filenames is picked (the one with the lowest numbered namespace) + * and its locale friendly name is put in pref_name. + * + * Return: n The number of $FILENAME attributes found + * -1 Error + */ +static int get_filenames(struct ufile *file, ntfs_volume* vol) +{ + ATTR_RECORD *rec; + FILE_NAME_ATTR *attr; + ntfs_attr_search_ctx *ctx; + struct filename *name; + int count = 0; + int space = 4; + + if (!file) + return -1; + + ctx = ntfs_attr_get_search_ctx(NULL, file->mft); + if (!ctx) + return -1; + + while ((rec = find_attribute(AT_FILE_NAME, ctx))) { + /* We know this will always be resident. */ + attr = (FILE_NAME_ATTR *)((char *)rec + + le16_to_cpu(rec->value_offset)); + + name = calloc(1, sizeof(*name)); + if (!name) { + ntfs_log_error("ERROR: Couldn't allocate memory in " + "get_filenames().\n"); + count = -1; + break; + } + + name->uname = attr->file_name; + name->uname_len = attr->file_name_length; + name->name_space = attr->file_name_type; + name->size_alloc = sle64_to_cpu(attr->allocated_size); + name->size_data = sle64_to_cpu(attr->data_size); + name->flags = attr->file_attributes; + + name->date_c = ntfs2timespec(attr->creation_time).tv_sec; + name->date_a = ntfs2timespec(attr->last_data_change_time).tv_sec; + name->date_m = ntfs2timespec(attr->last_mft_change_time).tv_sec; + name->date_r = ntfs2timespec(attr->last_access_time).tv_sec; + + if (ntfs_ucstombs(name->uname, name->uname_len, &name->name, + 0) < 0) { + ntfs_log_debug("ERROR: Couldn't translate filename to " + "current locale.\n"); + } + + name->parent_name = NULL; + + if (opts.parent) { + name->parent_mref = attr->parent_directory; + get_parent_name(name, vol); + } + + if (name->name_space < space) { + file->pref_name = name->name; + file->pref_pname = name->parent_name; + space = name->name_space; + } + + file->max_size = max(file->max_size, name->size_alloc); + file->max_size = max(file->max_size, name->size_data); + + ntfs_list_add_tail(&name->list, &file->name); + count++; + } + + if (!count) { + name = rescue_name(file->mft,ctx); + if (name) { + /* a name was recovered, get missing attributes */ + file->pref_name = name->name; + ntfs_attr_reinit_search_ctx(ctx); + rec = find_attribute(AT_STANDARD_INFORMATION, ctx); + if (rec) { + attr = (FILE_NAME_ATTR *)((char *)rec + + le16_to_cpu(rec->value_offset)); + name->flags = attr->file_attributes; + + name->date_c = ntfs2timespec(attr->creation_time).tv_sec; + name->date_a = ntfs2timespec(attr->last_data_change_time).tv_sec; + name->date_m = ntfs2timespec(attr->last_mft_change_time).tv_sec; + name->date_r = ntfs2timespec(attr->last_access_time).tv_sec; + } + rec = find_attribute(AT_DATA, ctx); + if (rec) { + attr = (FILE_NAME_ATTR *)((char *)rec + + le16_to_cpu(rec->value_offset)); + name->size_alloc = sle64_to_cpu(attr->allocated_size); + name->size_data = sle64_to_cpu(attr->data_size); + } + ntfs_list_add_tail(&name->list, &file->name); + count++; + } + } + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("File has %d names.\n", count); + return count; +} + +/** + * get_data - Read an MFT Record's $DATA attributes + * @file: The file object to work with + * @vol: An ntfs volume obtained from ntfs_mount + * + * A file may have more than one data stream. All files will have an unnamed + * data stream which contains the file's data. Some Windows applications store + * extra information in a separate stream. + * + * The streams that are found are put in data objects and added to a linked + * list of data streams in the file object. + * + * Return: n The number of $FILENAME attributes found + * -1 Error + */ +static int get_data(struct ufile *file, ntfs_volume *vol) +{ + ATTR_RECORD *rec; + ntfs_attr_search_ctx *ctx; + int count = 0; + struct data *data; + + if (!file) + return -1; + + ctx = ntfs_attr_get_search_ctx(NULL, file->mft); + if (!ctx) + return -1; + + while ((rec = find_attribute(AT_DATA, ctx))) { + data = calloc(1, sizeof(*data)); + if (!data) { + ntfs_log_error("ERROR: Couldn't allocate memory in " + "get_data().\n"); + count = -1; + break; + } + + data->resident = !rec->non_resident; + data->compressed = (rec->flags & ATTR_IS_COMPRESSED) ? 1 : 0; + data->encrypted = (rec->flags & ATTR_IS_ENCRYPTED) ? 1 : 0; + + if (rec->name_length) { + data->uname = (ntfschar *)((char *)rec + + le16_to_cpu(rec->name_offset)); + data->uname_len = rec->name_length; + + if (ntfs_ucstombs(data->uname, data->uname_len, + &data->name, 0) < 0) { + ntfs_log_error("ERROR: Cannot translate name " + "into current locale.\n"); + } + } + + if (data->resident) { + data->size_data = le32_to_cpu(rec->value_length); + data->data = (char*)rec + + le16_to_cpu(rec->value_offset); + } else { + data->size_alloc = sle64_to_cpu(rec->allocated_size); + data->size_data = sle64_to_cpu(rec->data_size); + data->size_init = sle64_to_cpu(rec->initialized_size); + data->size_vcn = sle64_to_cpu(rec->highest_vcn) + 1; + } + + data->runlist = ntfs_mapping_pairs_decompress(vol, rec, NULL); + if (!data->runlist) { + ntfs_log_debug("Couldn't decompress the data runs.\n"); + } + + file->max_size = max(file->max_size, data->size_data); + file->max_size = max(file->max_size, data->size_init); + + ntfs_list_add_tail(&data->list, &file->data); + count++; + } + + ntfs_attr_put_search_ctx(ctx); + ntfs_log_debug("File has %d data streams.\n", count); + return count; +} + +/** + * read_record - Read an MFT record into memory + * @vol: An ntfs volume obtained from ntfs_mount + * @record: The record number to read + * + * Read the specified MFT record and gather as much information about it as + * possible. + * + * Return: Pointer A ufile object containing the results + * NULL Error + */ +static struct ufile * read_record(ntfs_volume *vol, long long record) +{ + ATTR_RECORD *attr10, *attr20, *attr90; + struct ufile *file; + ntfs_attr *mft; + u32 log_levels; + + if (!vol) + return NULL; + + file = calloc(1, sizeof(*file)); + if (!file) { + ntfs_log_error("ERROR: Couldn't allocate memory in read_record()\n"); + return NULL; + } + + NTFS_INIT_LIST_HEAD(&file->name); + NTFS_INIT_LIST_HEAD(&file->data); + file->inode = record; + + file->mft = malloc(vol->mft_record_size); + if (!file->mft) { + ntfs_log_error("ERROR: Couldn't allocate memory in read_record()\n"); + free_file(file); + return NULL; + } + + mft = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (!mft) { + ntfs_log_perror("ERROR: Couldn't open $MFT/$DATA"); + free_file(file); + return NULL; + } + + if (ntfs_attr_mst_pread(mft, vol->mft_record_size * record, 1, vol->mft_record_size, file->mft) < 1) { + ntfs_log_error("ERROR: Couldn't read MFT Record %lld.\n", record); + ntfs_attr_close(mft); + free_file(file); + return NULL; + } + + ntfs_attr_close(mft); + mft = NULL; + + /* disable errors logging, while examining suspicious records */ + log_levels = ntfs_log_clear_levels(NTFS_LOG_LEVEL_PERROR); + attr10 = find_first_attribute(AT_STANDARD_INFORMATION, file->mft); + attr20 = find_first_attribute(AT_ATTRIBUTE_LIST, file->mft); + attr90 = find_first_attribute(AT_INDEX_ROOT, file->mft); + + ntfs_log_debug("Attributes present: %s %s %s.\n", attr10?"0x10":"", + attr20?"0x20":"", attr90?"0x90":""); + + if (attr10) { + STANDARD_INFORMATION *si; + si = (STANDARD_INFORMATION *) ((char *) attr10 + le16_to_cpu(attr10->value_offset)); + file->date = ntfs2timespec(si->last_data_change_time).tv_sec; + } + + if (attr20 || !attr10) + file->attr_list = 1; + if (attr90) + file->directory = 1; + + if (get_filenames(file, vol) < 0) { + ntfs_log_error("ERROR: Couldn't get filenames.\n"); + } + if (get_data(file, vol) < 0) { + ntfs_log_error("ERROR: Couldn't get data streams.\n"); + } + /* restore errors logging */ + ntfs_log_set_levels(log_levels); + + return file; +} + +/** + * calc_percentage - Calculate how much of the file is recoverable + * @file: The file object to work with + * @vol: An ntfs volume obtained from ntfs_mount + * + * Read through all the $DATA streams and determine if each cluster in each + * stream is still free disk space. This is just measuring the potential for + * recovery. The data may have still been overwritten by a another file which + * was then deleted. + * + * Files with a resident $DATA stream will have a 100% potential. + * + * N.B. If $DATA attribute spans more than one MFT record (i.e. badly + * fragmented) then only the data in this segment will be used for the + * calculation. + * + * N.B. Currently, compressed and encrypted files cannot be recovered, so they + * will return 0%. + * + * Return: n The percentage of the file that _could_ be recovered + * -1 Error + */ +static int calc_percentage(struct ufile *file, ntfs_volume *vol) +{ + runlist_element *rl = NULL; + struct ntfs_list_head *pos; + struct data *data; + long long i, j; + long long start, end; + int clusters_inuse, clusters_free; + int percent = 0; + + if (!file || !vol) + return -1; + + if (file->directory) { + ntfs_log_debug("Found a directory: not recoverable.\n"); + return 0; + } + + if (ntfs_list_empty(&file->data)) { + ntfs_log_verbose("File has no data streams.\n"); + return 0; + } + + ntfs_list_for_each(pos, &file->data) { + data = ntfs_list_entry(pos, struct data, list); + clusters_inuse = 0; + clusters_free = 0; + + if (data->encrypted) { + ntfs_log_verbose("File is encrypted, recovery is " + "impossible.\n"); + continue; + } + + if (data->compressed) { + ntfs_log_verbose("File is compressed, recovery not yet " + "implemented.\n"); + continue; + } + + if (data->resident) { + ntfs_log_verbose("File is resident, therefore " + "recoverable.\n"); + percent = 100; + data->percent = 100; + continue; + } + + rl = data->runlist; + if (!rl) { + ntfs_log_verbose("File has no runlist, hence no data." + "\n"); + continue; + } + + if (rl[0].length <= 0) { + ntfs_log_verbose("File has an empty runlist, hence no " + "data.\n"); + continue; + } + + if (rl[0].lcn == LCN_RL_NOT_MAPPED) { /* extended mft record */ + ntfs_log_verbose("Missing segment at beginning, %lld " + "clusters\n", (long long)rl[0].length); + clusters_inuse += rl[0].length; + rl++; + } + + for (i = 0; rl[i].length > 0; i++) { + if (rl[i].lcn == LCN_RL_NOT_MAPPED) { + ntfs_log_verbose("Missing segment at end, %lld " + "clusters\n", + (long long)rl[i].length); + clusters_inuse += rl[i].length; + continue; + } + + if (rl[i].lcn == LCN_HOLE) { + clusters_free += rl[i].length; + continue; + } + + start = rl[i].lcn; + end = rl[i].lcn + rl[i].length; + + for (j = start; j < end; j++) { + if (utils_cluster_in_use(vol, j)) + clusters_inuse++; + else + clusters_free++; + } + } + + if ((clusters_inuse + clusters_free) == 0) { + ntfs_log_error("ERROR: Unexpected error whilst " + "calculating percentage for inode %lld\n", + file->inode); + continue; + } + + data->percent = (clusters_free * 100) / + (clusters_inuse + clusters_free); + + percent = max(percent, data->percent); + } + + ntfs_log_verbose("File is %d%% recoverable\n", percent); + return percent; +} + +/** + * dump_record - Print everything we know about an MFT record + * @file: The file to work with + * + * Output the contents of the file object. This will print everything that has + * been read from the MFT record, or implied by various means. + * + * Because of the redundant nature of NTFS, there will be some duplication of + * information, though it will have been read from different sources. + * + * N.B. If the filename is missing, or couldn't be converted to the current + * locale, "<none>" will be displayed. + * + * Return: none + */ +static void dump_record(struct ufile *file) +{ + char buffer[20]; + struct ntfs_list_head *item; + int i; + + if (!file) + return; + + ntfs_log_quiet("MFT Record %lld\n", file->inode); + ntfs_log_quiet("Type: %s\n", (file->directory) ? "Directory" : "File"); + strftime(buffer, sizeof(buffer), "%F %R", localtime(&file->date)); + ntfs_log_quiet("Date: %s\n", buffer); + + if (file->attr_list) + ntfs_log_quiet("Metadata may span more than one MFT record\n"); + + ntfs_list_for_each(item, &file->name) { + struct filename *f = + ntfs_list_entry(item, struct filename, list); + + ntfs_log_quiet("Filename: (%d) %s\n", f->name_space, f->name); + ntfs_log_quiet("File Flags: "); + if (f->flags & FILE_ATTR_SYSTEM) + ntfs_log_quiet("System "); + if (f->flags & FILE_ATTR_DIRECTORY) + ntfs_log_quiet("Directory "); + if (f->flags & FILE_ATTR_SPARSE_FILE) + ntfs_log_quiet("Sparse "); + if (f->flags & FILE_ATTR_REPARSE_POINT) + ntfs_log_quiet("Reparse "); + if (f->flags & FILE_ATTR_COMPRESSED) + ntfs_log_quiet("Compressed "); + if (f->flags & FILE_ATTR_ENCRYPTED) + ntfs_log_quiet("Encrypted "); + if (!(f->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_DIRECTORY | + FILE_ATTR_SPARSE_FILE | FILE_ATTR_REPARSE_POINT | + FILE_ATTR_COMPRESSED | FILE_ATTR_ENCRYPTED))) { + ntfs_log_quiet("%s", NONE); + } + + ntfs_log_quiet("\n"); + + if (opts.parent) { + ntfs_log_quiet("Parent: %s\n", f->parent_name ? + f->parent_name : "<non-determined>"); + } + + ntfs_log_quiet("Size alloc: %lld\n", f->size_alloc); + ntfs_log_quiet("Size data: %lld\n", f->size_data); + + strftime(buffer, sizeof(buffer), "%F %R", + localtime(&f->date_c)); + ntfs_log_quiet("Date C: %s\n", buffer); + strftime(buffer, sizeof(buffer), "%F %R", + localtime(&f->date_a)); + ntfs_log_quiet("Date A: %s\n", buffer); + strftime(buffer, sizeof(buffer), "%F %R", + localtime(&f->date_m)); + ntfs_log_quiet("Date M: %s\n", buffer); + strftime(buffer, sizeof(buffer), "%F %R", + localtime(&f->date_r)); + ntfs_log_quiet("Date R: %s\n", buffer); + } + + ntfs_log_quiet("Data Streams:\n"); + ntfs_list_for_each(item, &file->data) { + struct data *d = ntfs_list_entry(item, struct data, list); + ntfs_log_quiet("Name: %s\n", (d->name) ? d->name : UNNAMED); + ntfs_log_quiet("Flags: "); + if (d->resident) ntfs_log_quiet("Resident\n"); + if (d->compressed) ntfs_log_quiet("Compressed\n"); + if (d->encrypted) ntfs_log_quiet("Encrypted\n"); + if (!d->resident && !d->compressed && !d->encrypted) + ntfs_log_quiet("None\n"); + else + ntfs_log_quiet("\n"); + + ntfs_log_quiet("Size alloc: %lld\n", d->size_alloc); + ntfs_log_quiet("Size data: %lld\n", d->size_data); + ntfs_log_quiet("Size init: %lld\n", d->size_init); + ntfs_log_quiet("Size vcn: %lld\n", d->size_vcn); + + ntfs_log_quiet("Data runs:\n"); + if ((!d->runlist) || (d->runlist[0].length <= 0)) { + ntfs_log_quiet(" None\n"); + } else { + for (i = 0; d->runlist[i].length > 0; i++) { + ntfs_log_quiet(" %lld @ %lld\n", + (long long)d->runlist[i].length, + (long long)d->runlist[i].lcn); + } + } + + ntfs_log_quiet("Amount potentially recoverable %d%%\n", + d->percent); + } + + ntfs_log_quiet("________________________________________\n\n"); +} + +/** + * list_record - Print a one line summary of the file + * @file: The file to work with + * + * Print a one line description of a file. + * + * Inode Flags %age Date Time Size Filename + * + * The output will contain the file's inode number (MFT Record), some flags, + * the percentage of the file that is recoverable, the last modification date, + * the size and the filename. + * + * The flags are F/D = File/Directory, N/R = Data is (Non-)Resident, + * C = Compressed, E = Encrypted, ! = Metadata may span multiple records. + * + * N.B. The file size is stored in many forms in several attributes. This + * display the largest it finds. + * + * N.B. If the filename is missing, or couldn't be converted to the current + * locale, "<none>" will be displayed. + * + * Return: none + */ +static void list_record(struct ufile *file) +{ + char buffer[20]; + struct ntfs_list_head *item; + const char *name = NULL; + long long size = 0; + int percent = 0; + + char flagd = '.', flagr = '.', flagc = '.', flagx = '.'; + + strftime(buffer, sizeof(buffer), "%F %R", localtime(&file->date)); + + if (file->attr_list) + flagx = '!'; + + if (file->directory) + flagd = 'D'; + else + flagd = 'F'; + + ntfs_list_for_each(item, &file->data) { + struct data *d = ntfs_list_entry(item, struct data, list); + + if (!d->name) { + if (d->resident) + flagr = 'R'; + else + flagr = 'N'; + if (d->compressed) + flagc = 'C'; + if (d->encrypted) + flagc = 'E'; + + percent = max(percent, d->percent); + } + + size = max(size, d->size_data); + size = max(size, d->size_init); + } + + if (file->pref_name) + name = file->pref_name; + else + name = NONE; + + ntfs_log_quiet("%-8lld %c%c%c%c %3d%% %s %9lld %s\n", + file->inode, flagd, flagr, flagc, flagx, + percent, buffer, size, name); + +} + +/** + * name_match - Does a file have a name matching a regex + * @re: The regular expression object + * @file: The file to be tested + * + * Iterate through the file's $FILENAME attributes and compare them against the + * regular expression, created with regcomp. + * + * Return: 1 There is a matching filename. + * 0 There is no match. + */ +static int name_match(regex_t *re, struct ufile *file) +{ + struct ntfs_list_head *item; + int result; + + if (!re || !file) + return 0; + + ntfs_list_for_each(item, &file->name) { + struct filename *f = + ntfs_list_entry(item, struct filename, list); + + if (!f->name) + continue; +#ifdef HAVE_REGEX_H + result = regexec(re, f->name, 0, NULL, 0); +#else + result = regexec(re, f->uname, f->uname_len, NULL, 0); +#endif + if (result < 0) { + ntfs_log_perror("Couldn't compare filename with regex"); + return 0; + } else if (result == REG_NOERROR) { + ntfs_log_debug("Found a matching filename.\n"); + return 1; + } + } + + ntfs_log_debug("Filename '%s' doesn't match regex.\n", file->pref_name); + return 0; +} + +/** + * write_data - Write out a block of data + * @fd: File descriptor to write to + * @buffer: Data to write + * @bufsize: Amount of data to write + * + * Write a block of data to a file descriptor. + * + * Return: -1 Error, something went wrong + * 0 Success, all the data was written + */ +static unsigned int write_data(int fd, const char *buffer, + unsigned int bufsize) +{ + ssize_t result1, result2; + + if (!buffer) { + errno = EINVAL; + return -1; + } + + result1 = write(fd, buffer, bufsize); + if ((result1 == (ssize_t) bufsize) || (result1 < 0)) + return result1; + + /* Try again with the rest of the buffer */ + buffer += result1; + bufsize -= result1; + + result2 = write(fd, buffer, bufsize); + if (result2 < 0) + return result1; + + return result1 + result2; +} + +/** + * create_pathname - Create a path/file from some components + * @dir: Directory in which to create the file (optional) + * @name: Filename to give the file (optional) + * @stream: Name of the stream (optional) + * @buffer: Store the result here + * @bufsize: Size of buffer + * + * Create a filename from various pieces. The output will be of the form: + * dir/file + * dir/file:stream + * file + * file:stream + * + * All the components are optional. If the name is missing, "unknown" will be + * used. If the directory is missing the file will be created in the current + * directory. If the stream name is present it will be appended to the + * filename, delimited by a colon. + * + * N.B. If the buffer isn't large enough the name will be truncated. + * + * Return: n Length of the allocated name + */ +static int create_pathname(const char *dir, const char *name, + const char *stream, char *buffer, int bufsize) +{ + if (!name) + name = UNKNOWN; + + if (dir) + if (stream) + snprintf(buffer, bufsize, "%s/%s:%s", dir, name, stream); + else + snprintf(buffer, bufsize, "%s/%s", dir, name); + else + if (stream) + snprintf(buffer, bufsize, "%s:%s", name, stream); + else + snprintf(buffer, bufsize, "%s", name); + + return strlen(buffer); +} + +/** + * open_file - Open a file to write to + * @pathname: Path, name and stream of the file to open + * + * Create a file and return the file descriptor. + * + * N.B. If option force is given and existing file will be overwritten. + * + * Return: -1 Error, failed to create the file + * n Success, this is the file descriptor + */ +static int open_file(const char *pathname) +{ + int flags; + + ntfs_log_verbose("Creating file: %s\n", pathname); + + if (opts.force) + flags = O_RDWR | O_CREAT | O_TRUNC; + else + flags = O_RDWR | O_CREAT | O_EXCL; +#ifdef HAVE_WINDOWS_H + flags ^= O_BINARY | O_RDWR | O_WRONLY; +#endif + + return open(pathname, flags, S_IRUSR | S_IWUSR); +} + +/** + * set_date - Set the file's date and time + * @pathname: Path and name of the file to alter + * @date: Date and time to set + * + * Give a file a particular date and time. + * + * Return: 1 Success, set the file's date and time + * 0 Error, failed to change the file's date and time + */ +static int set_date(const char *pathname, time_t date) +{ + struct utimbuf ut; + + if (!pathname) + return 0; + + ut.actime = date; + ut.modtime = date; + if (utime(pathname, &ut)) { + ntfs_log_error("ERROR: Couldn't set the file's date and time\n"); + return 0; + } + return 1; +} + +/** + * undelete_file - Recover a deleted file from an NTFS volume + * @vol: An ntfs volume obtained from ntfs_mount + * @inode: MFT Record number to be recovered + * + * Read an MFT Record and try an recover any data associated with it. Some of + * the clusters may be in use; these will be filled with zeros or the fill byte + * supplied in the options. + * + * Each data stream will be recovered and saved to a file. The file's name will + * be the original filename and it will be written to the current directory. + * Any named data stream will be saved as filename:streamname. + * + * The output file's name and location can be altered by using the command line + * options. + * + * N.B. We cannot tell if someone has overwritten some of the data since the + * file was deleted. + * + * Return: 0 Error, something went wrong + * 1 Success, the data was recovered + */ +static int undelete_file(ntfs_volume *vol, long long inode) +{ + char pathname[256]; + char *buffer = NULL; + unsigned int bufsize; + struct ufile *file; + int i, j; + long long start, end; + runlist_element *rl; + struct ntfs_list_head *item; + int fd = -1; + long long k; + int result = 0; + char *name; + long long cluster_count; /* I'll need this variable (see below). +mabs */ + + if (!vol) + return 0; + + /* try to get record */ + file = read_record(vol, inode); + if (!file || !file->mft) { + ntfs_log_error("Can't read info from mft record %lld.\n", inode); + return 0; + } + + /* if flag was not set, print file informations */ + if (avoid_duplicate_printing == 0) { + if (opts.verbose) { + dump_record(file); + } else { + list_record(file); + //ntfs_log_quiet("\n"); + } + } + + bufsize = vol->cluster_size; + buffer = malloc(bufsize); + if (!buffer) + goto free; + + /* calc_percentage() must be called before dump_record() or + * list_record(). Otherwise, when undeleting, a file will always be + * listed as 0% recoverable even if successfully undeleted. +mabs + */ + if (file->mft->flags & MFT_RECORD_IN_USE) { + ntfs_log_error("Record is in use by the mft\n"); + if (!opts.force) { + free(buffer); + free_file(file); + return 0; + } + ntfs_log_verbose("Forced to continue.\n"); + } + + if (calc_percentage(file, vol) == 0) { + ntfs_log_quiet("File has no recoverable data.\n"); + goto free; + } + + if (ntfs_list_empty(&file->data)) { + ntfs_log_quiet("File has no data. There is nothing to recover.\n"); + goto free; + } + + ntfs_list_for_each(item, &file->data) { + struct data *d = ntfs_list_entry(item, struct data, list); + char defname[sizeof(UNKNOWN) + 25]; + + if (opts.output) + name = opts.output; + else + if (file->pref_name) + name = file->pref_name; + else { + sprintf(defname,"%s%lld",UNKNOWN, + (long long)file->inode); + name = defname; + } + + create_pathname(opts.dest, name, d->name, pathname, sizeof(pathname)); + if (d->resident) { + fd = open_file(pathname); + if (fd < 0) { + ntfs_log_perror("Couldn't create file"); + goto free; + } + + ntfs_log_verbose("File has resident data.\n"); + if (write_data(fd, d->data, d->size_data) < d->size_data) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + + if (close(fd) < 0) { + ntfs_log_perror("Close failed"); + } + fd = -1; + } else { + rl = d->runlist; + if (!rl) { + ntfs_log_verbose("File has no runlist, hence no data.\n"); + continue; + } + + if (rl[0].length <= 0) { + ntfs_log_verbose("File has an empty runlist, hence no data.\n"); + continue; + } + + fd = open_file(pathname); + if (fd < 0) { + ntfs_log_perror("Couldn't create output file"); + goto free; + } + + if (rl[0].lcn == LCN_RL_NOT_MAPPED) { /* extended mft record */ + ntfs_log_verbose("Missing segment at beginning, %lld " + "clusters.\n", + (long long)rl[0].length); + memset(buffer, opts.fillbyte, bufsize); + for (k = 0; k < rl[0].length * vol->cluster_size; k += bufsize) { + if (write_data(fd, buffer, bufsize) < bufsize) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + } + } + + cluster_count = 0LL; + for (i = 0; rl[i].length > 0; i++) { + + if (rl[i].lcn == LCN_RL_NOT_MAPPED) { + ntfs_log_verbose("Missing segment at end, " + "%lld clusters.\n", + (long long)rl[i].length); + memset(buffer, opts.fillbyte, bufsize); + for (k = 0; k < rl[i].length * vol->cluster_size; k += bufsize) { + if (write_data(fd, buffer, bufsize) < bufsize) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + cluster_count++; + } + continue; + } + + if (rl[i].lcn == LCN_HOLE) { + ntfs_log_verbose("File has a sparse section.\n"); + memset(buffer, 0, bufsize); + for (k = 0; k < rl[i].length * vol->cluster_size; k += bufsize) { + if (write_data(fd, buffer, bufsize) < bufsize) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + } + continue; + } + + start = rl[i].lcn; + end = rl[i].lcn + rl[i].length; + + for (j = start; j < end; j++) { + if (utils_cluster_in_use(vol, j) && !opts.optimistic) { + memset(buffer, opts.fillbyte, bufsize); + if (write_data(fd, buffer, bufsize) < bufsize) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + } else { + if (ntfs_cluster_read(vol, j, 1, buffer) < 1) { + ntfs_log_perror("Read failed"); + close(fd); + goto free; + } + if (write_data(fd, buffer, bufsize) < bufsize) { + ntfs_log_perror("Write failed"); + close(fd); + goto free; + } + cluster_count++; + } + } + } + ntfs_log_quiet("\n"); + + /* + * The following block of code implements the --truncate option. + * Its semantics are as follows: + * IF opts.truncate is set AND data stream currently being recovered is + * non-resident AND data stream has no holes (100% recoverability) AND + * 0 <= (data->size_alloc - data->size_data) <= vol->cluster_size AND + * cluster_count * vol->cluster_size == data->size_alloc THEN file + * currently being written is truncated to data->size_data bytes before + * it's closed. + * This multiple checks try to ensure that only files with consistent + * values of size/occupied clusters are eligible for truncation. Note + * that resident streams need not be truncated, since the original code + * already recovers their exact length. +mabs + */ + if (opts.truncate) { + if (d->percent == 100 && d->size_alloc >= d->size_data && + (d->size_alloc - d->size_data) <= (long long)vol->cluster_size && + cluster_count * (long long)vol->cluster_size == d->size_alloc) { + if (ftruncate(fd, (off_t)d->size_data)) + ntfs_log_perror("Truncation failed"); + } else ntfs_log_quiet("Truncation not performed because file has an " + "inconsistent $MFT record.\n"); + } + + if (close(fd) < 0) { + ntfs_log_perror("Close failed"); + } + fd = -1; + + } + set_date(pathname, file->date); + if (d->name) + ntfs_log_quiet("Undeleted '%s:%s' successfully.\n", file->pref_name, d->name); + else + ntfs_log_quiet("Undeleted '%s' successfully.\n", file->pref_name); + } + result = 1; +free: + if (buffer) + free(buffer); + free_file(file); + return result; +} + +/** + * scan_disk - Search an NTFS volume for files that could be undeleted + * @vol: An ntfs volume obtained from ntfs_mount + * + * Read through all the MFT entries looking for deleted files. For each one + * determine how much of the data lies in unused disk space. + * + * The list can be filtered by name, size and date, using command line options. + * + * Return: -1 Error, something went wrong + * n Success, the number of recoverable files + */ +static int scan_disk(ntfs_volume *vol) +{ + s64 nr_mft_records; + const int BUFSIZE = 8192; + char *buffer = NULL; + int results = 0; + ntfs_attr *attr; + long long size; + long long bmpsize; + long long i; + int j, k, b; + int percent; + struct ufile *file; + regex_t re; + + if (!vol) + return -1; + + attr = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0); + if (!attr) { + ntfs_log_perror("ERROR: Couldn't open $MFT/$BITMAP"); + return -1; + } + NVolSetNoFixupWarn(vol); + bmpsize = attr->initialized_size; + + buffer = malloc(BUFSIZE); + if (!buffer) { + ntfs_log_error("ERROR: Couldn't allocate memory in scan_disk()\n"); + results = -1; + goto out; + } + + if (opts.match) { + int flags = REG_NOSUB; + + if (!opts.match_case) + flags |= REG_ICASE; + if (regcomp(&re, opts.match, flags)) { + ntfs_log_error("ERROR: Couldn't create a regex.\n"); + goto out; + } +#ifndef HAVE_REGEX_H + re->upcase = vol->upcase; + re->upcase_len = vol->upcase_len; +#endif + } + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + ntfs_log_quiet("Inode Flags %%age Date Time Size Filename\n"); + ntfs_log_quiet("-----------------------------------------------------------------------\n"); + for (i = 0; i < bmpsize; i += BUFSIZE) { + long long read_count = min((bmpsize - i), BUFSIZE); + size = ntfs_attr_pread(attr, i, read_count, buffer); + if (size < 0) + break; + + for (j = 0; j < size; j++) { + b = buffer[j]; + for (k = 0; k < 8; k++, b>>=1) { + if (((i+j)*8+k) >= nr_mft_records) + goto done; + if (b & 1) + continue; + file = read_record(vol, (i+j)*8+k); + if (!file) { + ntfs_log_error("Couldn't read MFT Record %lld.\n", + (long long)(i+j)*8+k); + continue; + } + + if ((opts.since > 0) && (file->date <= opts.since)) + goto skip; + if (opts.match && !name_match(&re, file)) + goto skip; + if (opts.size_begin && (opts.size_begin > file->max_size)) + goto skip; + if (opts.size_end && (opts.size_end < file->max_size)) + goto skip; + + percent = calc_percentage(file, vol); + if ((opts.percent == -1) || (percent >= opts.percent)) { + if (opts.verbose) + dump_record(file); + else + list_record(file); + + /* Was -u specified with no inode + so undelete file by regex */ + if (opts.mode == MODE_UNDELETE) { + if (!undelete_file(vol, file->inode)) + ntfs_log_verbose("ERROR: Failed to undelete " + "inode %lli\n!", + file->inode); + ntfs_log_info("\n"); + } + } + if (((opts.percent == -1) && (percent > 0)) || + ((opts.percent > 0) && (percent >= opts.percent))) { + results++; + } +skip: + free_file(file); + } + } + } +done: + ntfs_log_quiet("\nFiles with potentially recoverable content: %d\n", + results); +out: + if (opts.match) + regfree(&re); + free(buffer); + NVolClearNoFixupWarn(vol); + if (attr) + ntfs_attr_close(attr); + return results; +} + +/** + * copy_mft - Write a range of MFT Records to a file + * @vol: An ntfs volume obtained from ntfs_mount + * @mft_begin: First MFT Record to save + * @mft_end: Last MFT Record to save + * + * Read a number of MFT Records and write them to a file. + * + * Return: 0 Success, all the records were written + * 1 Error, something went wrong + */ +static int copy_mft(ntfs_volume *vol, long long mft_begin, long long mft_end) +{ + s64 nr_mft_records; + char pathname[256]; + ntfs_attr *mft; + char *buffer; + const char *name; + long long i; + int result = 1; + int fd; + + if (!vol) + return 1; + + if (mft_end < mft_begin) { + ntfs_log_error("Range to copy is backwards.\n"); + return 1; + } + + buffer = malloc(vol->mft_record_size); + if (!buffer) { + ntfs_log_error("Couldn't allocate memory in copy_mft()\n"); + return 1; + } + + mft = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (!mft) { + ntfs_log_perror("Couldn't open $MFT/$DATA"); + goto free; + } + + name = opts.output; + if (!name) { + name = MFTFILE; + ntfs_log_debug("No output filename, defaulting to '%s'.\n", + name); + } + + create_pathname(opts.dest, name, NULL, pathname, sizeof(pathname)); + fd = open_file(pathname); + if (fd < 0) { + ntfs_log_perror("Couldn't open output file '%s'", name); + goto attr; + } + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + mft_end = min(mft_end, nr_mft_records - 1); + + ntfs_log_debug("MFT records:\n"); + ntfs_log_debug("\tTotal: %8lld\n", nr_mft_records); + ntfs_log_debug("\tBegin: %8lld\n", mft_begin); + ntfs_log_debug("\tEnd: %8lld\n", mft_end); + + for (i = mft_begin; i <= mft_end; i++) { + if (ntfs_attr_pread(mft, vol->mft_record_size * i, + vol->mft_record_size, buffer) < vol->mft_record_size) { + ntfs_log_perror("Couldn't read MFT Record %lld", i); + goto close; + } + + if (write_data(fd, buffer, vol->mft_record_size) < vol->mft_record_size) { + ntfs_log_perror("Write failed"); + goto close; + } + } + + ntfs_log_verbose("Read %lld MFT Records\n", mft_end - mft_begin + 1); + result = 0; +close: + close(fd); +attr: + ntfs_attr_close(mft); +free: + free(buffer); + return result; +} + +/** + * handle_undelete + * + * Handles the undelete + */ +static int handle_undelete(ntfs_volume *vol) +{ + int result = 1; + int i; + unsigned long long inode; + + /* Check whether (an) inode(s) was specified or at least a regex! */ + if (nr_entries == 0) { + if (with_regex == 0) { + ntfs_log_error("ERROR: NO inode(s) AND NO match-regex " + "specified!\n"); + } else { + avoid_duplicate_printing= 1; + result = !scan_disk(vol); + if (result) + ntfs_log_verbose("ERROR: Failed to scan device " + "'%s'.\n", opts.device); + } + } else { + /* Normal undelete by specifying inode(s) */ + ntfs_log_quiet("Inode Flags %%age Date Size Filename\n"); + ntfs_log_quiet("---------------------------------------------------------------\n"); + + /* loop all given inodes */ + for (i = 0; i < nr_entries; i++) { + for (inode = ranges[i].begin; inode <= ranges[i].end; inode ++) { + /* Now undelete file */ + result = !undelete_file(vol, inode); + if (result) + ntfs_log_verbose("ERROR: Failed to " + "undelete inode %lli\n!", inode); + } + } + } + return (result); +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + ntfs_volume *vol; + int result = 1; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + with_regex = 0; + avoid_duplicate_printing = 0; + + result = parse_options(argc, argv); + if (result >= 0) + goto free; + + utils_set_locale(); + + vol = utils_mount_volume(opts.device, NTFS_MNT_RDONLY | + (opts.force ? NTFS_MNT_RECOVER : 0)); + if (!vol) + return 1; + + /* handling of the different modes */ + switch (opts.mode) { + /* Scanning */ + case MODE_SCAN: + result = !scan_disk(vol); + if (result) + ntfs_log_verbose("ERROR: Failed to scan device '%s'.\n", + opts.device); + break; + + /* Undelete-handling */ + case MODE_UNDELETE: + result= handle_undelete(vol); + break; + + /* Handling of copy mft */ + case MODE_COPY: + result = !copy_mft(vol, opts.mft_begin, opts.mft_end); + if (result) + ntfs_log_verbose("ERROR: Failed to read MFT blocks " + "%lld-%lld.\n", (long long)opts.mft_begin, + (long long)min((vol->mft_na->initialized_size >> + vol->mft_record_size_bits) , opts.mft_end)); + break; + default: + ; /* Cannot happen */ + } + + ntfs_umount(vol, FALSE); +free: + if (opts.match) + free(opts.match); + + return result; +} + diff --git a/ntfsprogs/ntfsundelete.h b/ntfsprogs/ntfsundelete.h new file mode 100755 index 0000000000000000000000000000000000000000..dd462f2c313c7179f177c7e9afb6a4ebabc8ee30 --- /dev/null +++ b/ntfsprogs/ntfsundelete.h @@ -0,0 +1,112 @@ +/* + * ntfsundelete - Part of the Linux-NTFS project. + * + * Copyright (c) 2002 Richard Russon + * Copyright (c) 2007 Yura Pakhuchiy + * + * This utility will recover deleted files from an NTFS volume. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFSUNDELETE_H_ +#define _NTFSUNDELETE_H_ + +#include "types.h" +#include "list.h" +#include "runlist.h" +#include "utils.h" + +enum optmode { + MODE_NONE = 0, + MODE_SCAN, + MODE_UNDELETE, + MODE_COPY, + MODE_ERROR +}; + +struct options { + char *device; /* Device/File to work with */ + enum optmode mode; /* Scan / Undelete / Copy */ + int percent; /* Minimum recoverability */ + int uinode; /* Undelete this inode */ + char *dest; /* Save file to this directory */ + char *output; /* With this filename */ + char *match; /* Pattern for filename matching */ + int match_case; /* Case sensitive matching */ + int truncate; /* Truncate files to exact size. */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int force; /* Override common sense */ + int optimistic; /* Undelete in-use clusters as well */ + int parent; /* Show parent directory */ + time_t since; /* Since this time */ + s64 size_begin; /* Range for file size */ + s64 size_end; + s64 mft_begin; /* Range for mft copy */ + s64 mft_end; + char fillbyte; /* Use for unrecoverable sections */ +}; + +struct filename { + struct ntfs_list_head list; /* Previous/Next links */ + ntfschar *uname; /* Filename in unicode */ + int uname_len; /* and its length */ + long long size_alloc; /* Allocated size (multiple of cluster size) */ + long long size_data; /* Actual size of data */ + FILE_ATTR_FLAGS flags; + time_t date_c; /* Time created */ + time_t date_a; /* altered */ + time_t date_m; /* mft record changed */ + time_t date_r; /* read */ + char *name; /* Filename in current locale */ + FILE_NAME_TYPE_FLAGS name_space; + leMFT_REF parent_mref; + char *parent_name; +}; + +struct data { + struct ntfs_list_head list; /* Previous/Next links */ + char *name; /* Stream name in current locale */ + ntfschar *uname; /* Unicode stream name */ + int uname_len; /* and its length */ + int resident; /* Stream is resident */ + int compressed; /* Stream is compressed */ + int encrypted; /* Stream is encrypted */ + long long size_alloc; /* Allocated size (multiple of cluster size) */ + long long size_data; /* Actual size of data */ + long long size_init; /* Initialised size, may be less than data size */ + long long size_vcn; /* Highest VCN in the data runs */ + runlist_element *runlist; /* Decoded data runs */ + int percent; /* Amount potentially recoverable */ + void *data; /* If resident, a pointer to the data */ +}; + +struct ufile { + long long inode; /* MFT record number */ + time_t date; /* Last modification date/time */ + struct ntfs_list_head name; /* A list of filenames */ + struct ntfs_list_head data; /* A list of data streams */ + char *pref_name; /* Preferred filename */ + char *pref_pname; /* parent filename */ + long long max_size; /* Largest size we find */ + int attr_list; /* MFT record may be one of many */ + int directory; /* MFT record represents a directory */ + MFT_RECORD *mft; /* Raw MFT record */ +}; + +#endif /* _NTFSUNDELETE_H_ */ + diff --git a/ntfsprogs/ntfswipe.8 b/ntfsprogs/ntfswipe.8 new file mode 100755 index 0000000000000000000000000000000000000000..6912d3f12548f50964df45be999fa595b3a6aa4c --- /dev/null +++ b/ntfsprogs/ntfswipe.8 @@ -0,0 +1,128 @@ +.\" Copyright (c) 2014 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSWIPE 8 "June 2014" "ntfs-3g 2015.3.14" +.SH NAME +ntfswipe \- overwrite unused space on an NTFS volume +.SH SYNOPSIS +\fBntfswipe\fR [\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfswipe +clears all or part of unused space on an NTFS volume by overwriting +with zeroes or random bytes. +.SH OPTIONS +Below is a summary of all the options that +.B ntfswipe +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-a\fR, \fB\-\-all\fR +Wipe all unused space. This may take significant time. +.TP +\fB\-b\fR, \fB\-\-bytes\fR BYTE-LIST +Define the allowed replacement bytes which are drawn randomly to overwrite +the unused space. BYTE-LIST is a comma-separated list of values in +range 0-255 expressed in octal, decimal or hexadecimal base. +.TP +\fB\-c\fR, \fB\-\-count\fR NUM +Define the number of times the unused space is to be overwritten. +.TP +\fB\-d\fR, \fB\-\-directory\fR +Wipe all the directory indexes, which may contain names of deleted files. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not using a mounted volume. +Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-i\fR, \fB\-\-info\fR +Display details about unused space. +.TP +\fB\-l\fR, \fB\-\-logfile\fR +Overwrite the logfile (update journal). +.TP +\fB\-m\fR, \fB\-\-mft\fR +Overwrite the unused space in the MFT (main file table, which contains the +file names, and the contents of short files). +.TP +\fB\-n\fR, \fB\-\-no-action\fR +Executes the wiping process without writing to device. +.TP +\fB\-p\fR, \fB\-\-pagefile\fR +Overwrite the Windows swap space. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-s\fR, \fB\-\-undel\fR +Overwrite the space which had been allocated to a file which has been deleted +recently and is still undeletable. +.TP +\fB\-t\fR, \fB\-\-tails\fR +Overwrite the space at the end of files which is unused, but allocated +because the allocations are always done by full clusters. +.TP +\fB\-u\fR, \fB\-\-unused\fR +Overwrite the space which is currently not allocated to any file (but +may have been used in the past). +.TP +\fB\-U\fR, \fB\-\-unused-fast\fR +Overwrite the space which is currently not allocated to any file, trying +not to overwrite the space not written to since the previous wiping. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license of +.BR ntfswipe . +.SH EXAMPLES +Wipe out all unused space in an NTFS volume. +.RS +.sp +.B ntfswipe -a /dev/sda1 +.sp +.RE +Wipe out all deleted file names from an NTFS volume. +.RS +.sp +.B ntfswipe -dms /dev/sda1 +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfswipe . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfswipe +was written by Richard Russon, Anton Altaparmakov and Yura Pakhuchiy. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfswipe +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfs-3g (8), +.BR ntfsls (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfswipe.8.in b/ntfsprogs/ntfswipe.8.in new file mode 100755 index 0000000000000000000000000000000000000000..08192580500a50199c7c62adf6f6db51f21c0691 --- /dev/null +++ b/ntfsprogs/ntfswipe.8.in @@ -0,0 +1,128 @@ +.\" Copyright (c) 2014 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFSWIPE 8 "June 2014" "ntfs-3g @VERSION@" +.SH NAME +ntfswipe \- overwrite unused space on an NTFS volume +.SH SYNOPSIS +\fBntfswipe\fR [\fIoptions\fR] \fIdevice\fR +.SH DESCRIPTION +.B ntfswipe +clears all or part of unused space on an NTFS volume by overwriting +with zeroes or random bytes. +.SH OPTIONS +Below is a summary of all the options that +.B ntfswipe +accepts. Nearly all options have two equivalent names. The short name is +preceded by +.B \- +and the long name is preceded by +.BR \-\- . +Any single letter options, that don't take an argument, can be combined into a +single command, e.g. +.B \-fv +is equivalent to +.BR "\-f \-v" . +Long named options can be abbreviated to any unique prefix of their name. +.TP +\fB\-a\fR, \fB\-\-all\fR +Wipe all unused space. This may take significant time. +.TP +\fB\-b\fR, \fB\-\-bytes\fR BYTE-LIST +Define the allowed replacement bytes which are drawn randomly to overwrite +the unused space. BYTE-LIST is a comma-separated list of values in +range 0-255 expressed in octal, decimal or hexadecimal base. +.TP +\fB\-c\fR, \fB\-\-count\fR NUM +Define the number of times the unused space is to be overwritten. +.TP +\fB\-d\fR, \fB\-\-directory\fR +Wipe all the directory indexes, which may contain names of deleted files. +.TP +\fB\-f\fR, \fB\-\-force\fR +This will override some sensible defaults, such as not using a mounted volume. +Use this option with caution. +.TP +\fB\-h\fR, \fB\-\-help\fR +Show a list of options with a brief description of each one. +.TP +\fB\-i\fR, \fB\-\-info\fR +Display details about unused space. +.TP +\fB\-l\fR, \fB\-\-logfile\fR +Overwrite the logfile (update journal). +.TP +\fB\-m\fR, \fB\-\-mft\fR +Overwrite the unused space in the MFT (main file table, which contains the +file names, and the contents of short files). +.TP +\fB\-n\fR, \fB\-\-no-action\fR +Executes the wiping process without writing to device. +.TP +\fB\-p\fR, \fB\-\-pagefile\fR +Overwrite the Windows swap space. +.TP +\fB\-q\fR, \fB\-\-quiet\fR +Suppress some debug/warning/error messages. +.TP +\fB\-s\fR, \fB\-\-undel\fR +Overwrite the space which had been allocated to a file which has been deleted +recently and is still undeletable. +.TP +\fB\-t\fR, \fB\-\-tails\fR +Overwrite the space at the end of files which is unused, but allocated +because the allocations are always done by full clusters. +.TP +\fB\-u\fR, \fB\-\-unused\fR +Overwrite the space which is currently not allocated to any file (but +may have been used in the past). +.TP +\fB\-U\fR, \fB\-\-unused-fast\fR +Overwrite the space which is currently not allocated to any file, trying +not to overwrite the space not written to since the previous wiping. +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Display more debug/warning/error messages. +.TP +\fB\-V\fR, \fB\-\-version\fR +Show the version number, copyright and license of +.BR ntfswipe . +.SH EXAMPLES +Wipe out all unused space in an NTFS volume. +.RS +.sp +.B ntfswipe -a /dev/sda1 +.sp +.RE +Wipe out all deleted file names from an NTFS volume. +.RS +.sp +.B ntfswipe -dms /dev/sda1 +.sp +.RE +.SH BUGS +There are no known problems with +.BR ntfswipe . +If you find a bug please send an email describing the problem to the +development team: +.br +.nh +ntfs\-3g\-devel@lists.sf.net +.hy +.SH AUTHORS +.B ntfswipe +was written by Richard Russon, Anton Altaparmakov and Yura Pakhuchiy. +It was ported to ntfs-3g by Erik Larsson. +.SH AVAILABILITY +.B ntfswipe +is part of the +.B ntfs-3g +package and is available from: +.br +.nh +http://www.tuxera.com/community/ +.hy +.SH SEE ALSO +.BR ntfs-3g (8), +.BR ntfsls (8), +.BR ntfsprogs (8) diff --git a/ntfsprogs/ntfswipe.c b/ntfsprogs/ntfswipe.c new file mode 100755 index 0000000000000000000000000000000000000000..658aaac08b4d86f15516c06c4d7dd88a0645e946 --- /dev/null +++ b/ntfsprogs/ntfswipe.c @@ -0,0 +1,2260 @@ +/** + * ntfswipe - Part of the Linux-NTFS project. + * + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2004 Yura Pakhuchiy + * + * This utility will overwrite unused space on an NTFS volume. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#else +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif + +#include "ntfswipe.h" +#include "types.h" +#include "volume.h" +#include "utils.h" +#include "debug.h" +#include "dir.h" +#include "mst.h" +/* #include "version.h" */ +#include "logging.h" +#include "list.h" +#include "mft.h" + +static const char *EXEC_NAME = "ntfswipe"; +static struct options opts; +static unsigned long int npasses = 0; + +struct filename { + char *parent_name; + struct ntfs_list_head list; /* Previous/Next links */ + ntfschar *uname; /* Filename in unicode */ + int uname_len; /* and its length */ + /* Allocated size (multiple of cluster size) */ + s64 size_alloc; + s64 size_data; /* Actual size of data */ + long long parent_mref; + FILE_ATTR_FLAGS flags; + time_t date_c; /* Time created */ + time_t date_a; /* altered */ + time_t date_m; /* mft record changed */ + time_t date_r; /* read */ + char *name; /* Filename in current locale */ + FILE_NAME_TYPE_FLAGS name_space; + char padding[7]; /* Unused: padding to 64 bit. */ +}; + +struct data { + struct ntfs_list_head list; /* Previous/Next links */ + char *name; /* Stream name in current locale */ + ntfschar *uname; /* Unicode stream name */ + int uname_len; /* and its length */ + int resident; /* Stream is resident */ + int compressed; /* Stream is compressed */ + int encrypted; /* Stream is encrypted */ + /* Allocated size (multiple of cluster size) */ + s64 size_alloc; + s64 size_data; /* Actual size of data */ + /* Initialised size, may be less than data size */ + s64 size_init; + VCN size_vcn; /* Highest VCN in the data runs */ + runlist_element *runlist; /* Decoded data runs */ + int percent; /* Amount potentially recoverable */ + void *data; /* If resident, a pointer to the data */ + char padding[4]; /* Unused: padding to 64 bit. */ +}; + +struct ufile { + s64 inode; /* MFT record number */ + time_t date; /* Last modification date/time */ + struct ntfs_list_head name; /* A list of filenames */ + struct ntfs_list_head data; /* A list of data streams */ + char *pref_name; /* Preferred filename */ + char *pref_pname; /* parent filename */ + s64 max_size; /* Largest size we find */ + int attr_list; /* MFT record may be one of many */ + int directory; /* MFT record represents a directory */ + MFT_RECORD *mft; /* Raw MFT record */ + char padding[4]; /* Unused: padding to 64 bit. */ +}; + +#define NPAT 22 + +/* Taken from `shred' source */ +static const unsigned int patterns[NPAT] = { + 0x000, 0xFFF, /* 1-bit */ + 0x555, 0xAAA, /* 2-bit */ + 0x249, 0x492, 0x6DB, 0x924, 0xB6D, 0xDB6, /* 3-bit */ + 0x111, 0x222, 0x333, 0x444, 0x666, 0x777, + 0x888, 0x999, 0xBBB, 0xCCC, 0xDDD, 0xEEE /* 4-bit */ +}; + + +/** + * version - Print version information about the program + * + * Print a copyright statement and a brief description of the program. + * + * Return: none + */ +static void version(void) +{ + ntfs_log_info("\n%s v%s (libntfs-3g) - Overwrite the unused space on an NTFS " + "Volume.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("Copyright (c) 2002-2005 Richard Russon\n"); + ntfs_log_info("Copyright (c) 2004 Yura Pakhuchiy\n"); + ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); +} + +/** + * usage - Print a list of the parameters to the program + * + * Print a list of the parameters and options for the program. + * + * Return: none + */ +static void usage(void) +{ + ntfs_log_info("\nUsage: %s [options] device\n" + " -i --info Show volume information (default)\n" + "\n" + " -d --directory Wipe directory indexes\n" + " -l --logfile Wipe the logfile (journal)\n" + " -m --mft Wipe mft space\n" + " -p --pagefile Wipe pagefile (swap space)\n" + " -t --tails Wipe file tails\n" + " -u --unused Wipe unused clusters\n" + " -U --unused-fast Wipe unused clusters (fast)\n" + " -s --undel Wipe undelete data\n" + "\n" + " -a --all Wipe all unused space\n" + "\n" + " -c num --count num Number of times to write(default = 1)\n" + " -b list --bytes list List of values to write(default = 0)\n" + "\n" + " -n --no-action Do not write to disk\n" + " -f --force Use less caution\n" + " -q --quiet Less output\n" + " -v --verbose More output\n" + " -V --version Version information\n" + " -h --help Print this help\n\n", + EXEC_NAME); + ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); +} + +/** + * parse_list - Read a comma-separated list of numbers + * @list: The comma-separated list of numbers + * @result: Store the parsed list here (must be freed by caller) + * + * Read a comma-separated list of numbers and allocate an array of ints to store + * them in. The numbers can be in decimal, octal or hex. + * + * N.B. The caller must free the memory returned in @result. + * N.B. If the function fails, @result is not changed. + * + * Return: 0 Error, invalid string + * n Success, the count of numbers parsed + */ +static int parse_list(char *list, int **result) +{ + char *ptr; + char *end; + int i; + int count; + int *mem = NULL; + + if (!list || !result) + return 0; + + for (count = 0, ptr = list; ptr; ptr = strchr(ptr+1, ',')) + count++; + + mem = malloc((count+1) * sizeof(int)); + if (!mem) { + ntfs_log_error("Couldn't allocate memory in parse_list().\n"); + return 0; + } + + memset(mem, 0xFF, (count+1) * sizeof(int)); + + for (ptr = list, i = 0; i < count; i++) { + + end = NULL; + mem[i] = strtol(ptr, &end, 0); + + if (!end || (end == ptr) || ((*end != ',') && (*end != 0))) { + ntfs_log_error("Invalid list '%s'\n", list); + free(mem); + return 0; + } + + if ((mem[i] < 0) || (mem[i] > 255)) { + ntfs_log_error("Bytes must be in range 0-255.\n"); + free(mem); + return 0; + } + + ptr = end + 1; + } + + ntfs_log_debug("Parsing list '%s' - ", list); + for (i = 0; i <= count; i++) + ntfs_log_debug("0x%02x ", mem[i]); + ntfs_log_debug("\n"); + + *result = mem; + return count; +} + +/** + * parse_options - Read and validate the programs command line + * + * Read the command line, verify the syntax and parse the options. + * This function is very long, but quite simple. + * + * Return: 1 Success + * 0 Error, one or more problems + */ +static int parse_options(int argc, char *argv[]) +{ + static const char *sopt = "-ab:c:dfh?ilmnpqtuUvVs"; + static struct option lopt[] = { + { "all", no_argument, NULL, 'a' }, + { "bytes", required_argument, NULL, 'b' }, + { "count", required_argument, NULL, 'c' }, + { "directory", no_argument, NULL, 'd' }, + { "force", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "info", no_argument, NULL, 'i' }, + { "logfile", no_argument, NULL, 'l' }, + { "mft", no_argument, NULL, 'm' }, + { "no-action", no_argument, NULL, 'n' }, + //{ "no-wait", no_argument, NULL, 0 }, + { "pagefile", no_argument, NULL, 'p' }, + { "quiet", no_argument, NULL, 'q' }, + { "tails", no_argument, NULL, 't' }, + { "unused", no_argument, NULL, 'u' }, + { "unused-fast",no_argument, NULL, 'U' }, + { "undel", no_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int c = -1; + char *end; + int err = 0; + int ver = 0; + int help = 0; + int levels = 0; + + opterr = 0; /* We'll handle the errors, thank you. */ + + opts.count = 1; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = argv[optind-1]; + } else { + opts.device = NULL; + err++; + } + break; + + case 'i': + opts.info++; /* and fall through */ + case 'a': + opts.directory++; + opts.logfile++; + opts.mft++; + opts.pagefile++; + opts.tails++; + opts.unused++; + opts.undel++; + break; + case 'b': + if (!opts.bytes) { + if (!parse_list(optarg, &opts.bytes)) + err++; + } else { + err++; + } + break; + case 'c': + if (opts.count == 1) { + end = NULL; + opts.count = strtol(optarg, &end, 0); + if (end && *end) + err++; + } else { + err++; + } + break; + case 'd': + opts.directory++; + break; + case 'f': + opts.force++; + break; + case 'h': + help++; + break; + case 'l': + opts.logfile++; + break; + case 'm': + opts.mft++; + break; + case 'n': + opts.noaction++; + break; + case 'p': + opts.pagefile++; + break; + case 'q': + opts.quiet++; + ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); + break; + case 's': + opts.undel++; + break; + case 't': + opts.tails++; + break; + case 'u': + opts.unused++; + break; + case 'U': + opts.unused_fast++; + break; + case 'v': + opts.verbose++; + ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); + break; + case 'V': + ver++; + break; + case '?': + if (strncmp (argv[optind-1], "--log-", 6) == 0) { + if (!ntfs_log_parse_option (argv[optind-1])) + err++; + break; + } + /* fall through */ + default: + if ((optopt == 'b') || (optopt == 'c')) { + ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]); + } else { + ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); + } + err++; + break; + } + } + + /* Make sure we're in sync with the log levels */ + levels = ntfs_log_get_levels(); + if (levels & NTFS_LOG_LEVEL_VERBOSE) + opts.verbose++; + if (!(levels & NTFS_LOG_LEVEL_QUIET)) + opts.quiet++; + + if (help || ver) { + opts.quiet = 0; + } else { + if (opts.device == NULL) { + if (argc > 1) + ntfs_log_error("You must specify exactly one device.\n"); + err++; + } + + if (opts.quiet && opts.verbose) { + ntfs_log_error("You may not use --quiet and --verbose at the same time.\n"); + err++; + } + + /* + if (opts.info && (opts.unused || opts.tails || opts.mft || opts.directory)) { + ntfs_log_error("You may not use any other options with --info.\n"); + err++; + } + */ + + if ((opts.count < 1) || (opts.count > 100)) { + ntfs_log_error("The iteration count must be between 1 and 100.\n"); + err++; + } + + /* Create a default list */ + if (!opts.bytes) { + opts.bytes = malloc(2 * sizeof(int)); + if (opts.bytes) { + opts.bytes[0] = 0; + opts.bytes[1] = -1; + } else { + ntfs_log_error("Couldn't allocate memory for byte list.\n"); + err++; + } + } + + if (!opts.directory && !opts.logfile && !opts.mft && + !opts.pagefile && !opts.tails && !opts.unused && + !opts.unused_fast && !opts.undel) { + opts.info = 1; + } + } + + if (ver) + version(); + if (help || err) + usage(); + + /* tri-state 0 : done, 1 : error, -1 : proceed */ + return (err ? 1 : (help || ver ? 0 : -1)); +} + +/** + * wipe_unused - Wipe unused clusters + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * Read $Bitmap and wipe any clusters that are marked as not in use. + * + * Return: >0 Success, the attribute was wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_unused(ntfs_volume *vol, int byte, enum action act) +{ + s64 i; + s64 total = 0; + s64 result = 0; + u8 *buffer = NULL; + + if (!vol || (byte < 0)) + return -1; + + if (act != act_info) { + buffer = malloc(vol->cluster_size); + if (!buffer) { + ntfs_log_error("malloc failed\n"); + return -1; + } + memset(buffer, byte, vol->cluster_size); + } + + for (i = 0; i < vol->nr_clusters; i++) { + if (utils_cluster_in_use(vol, i)) { + //ntfs_log_verbose("cluster %lld is in use\n", i); + continue; + } + + if (act == act_wipe) { + //ntfs_log_verbose("cluster %lld is not in use\n", i); + result = ntfs_pwrite(vol->dev, vol->cluster_size * i, vol->cluster_size, buffer); + if (result != vol->cluster_size) { + ntfs_log_error("write failed\n"); + goto free; + } + } + + total += vol->cluster_size; + } + + ntfs_log_quiet("wipe_unused 0x%02x, %lld bytes\n", byte, (long long)total); +free: + free(buffer); + return total; +} + +/** + * wipe_unused_fast - Faster wipe unused clusters + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * Read $Bitmap and wipe any clusters that are marked as not in use. + * + * - read/write on a block basis (64 clusters, arbitrary) + * - skip of fully used block + * - skip non-used block already wiped + * + * Return: >0 Success, the attribute was wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_unused_fast(ntfs_volume *vol, int byte, enum action act) +{ + s64 i; + s64 total = 0; + s64 unused = 0; + s64 result; + u8 *buffer; + u8 *big_buffer; + u32 *u32_buffer; + u32 u32_bytes; + unsigned int blksize; + unsigned int j,k; + BOOL wipe_needed; + + if (!vol || (byte < 0)) + return -1; + + big_buffer = (u8*)malloc(vol->cluster_size*64); + if (!big_buffer) { + ntfs_log_error("malloc failed\n"); + return -1; + } + + for (i = 0; i < vol->nr_clusters; i+=64) { + blksize = vol->nr_clusters - i; + if (blksize > 64) + blksize = 64; + /* if all clusters in this block are used, ignore the block */ + result = 0; + for (j = 0; j < blksize; j++) { + if (utils_cluster_in_use(vol, i+j)) + result++; + } + unused += (blksize - result) * vol->cluster_size; + + if (result == blksize) { + continue; + } + /* + * if all unused clusters in this block are already wiped, + * ignore the block + */ + if (ntfs_pread(vol->dev, vol->cluster_size * i, + vol->cluster_size * blksize, big_buffer) + != vol->cluster_size * blksize) { + ntfs_log_error("Read failed at cluster %lld\n", + (long long)i); + goto free; + } + + result = 0; + wipe_needed = FALSE; + u32_bytes = (byte & 255)*0x01010101; + buffer = big_buffer; + for (j = 0; (j < blksize) && !wipe_needed; j++) { + u32_buffer = (u32*)buffer; + if (!utils_cluster_in_use(vol, i+j)) { + for (k = 0; (k < vol->cluster_size) + && (*u32_buffer++ == u32_bytes); k+=4) { + } + if (k < vol->cluster_size) + wipe_needed = TRUE; + } + buffer += vol->cluster_size; + } + + if (!wipe_needed) { + continue; + } + /* else wipe unused clusters in the block */ + buffer = big_buffer; + + for (j = 0; j < blksize; j++) { + if (!utils_cluster_in_use(vol, i+j)) { + memset(buffer, byte, vol->cluster_size); + total += vol->cluster_size; + } + buffer += vol->cluster_size; + } + + if ((act == act_wipe) + && (ntfs_pwrite(vol->dev, vol->cluster_size * i, + vol->cluster_size * blksize, big_buffer) + != vol->cluster_size * blksize)) { + ntfs_log_error("Write failed at cluster %lld\n", + (long long)i); + goto free; + } + } + + ntfs_log_quiet("wipe_unused_fast 0x%02x, %lld bytes" + " already wiped, %lld more bytes wiped\n", + byte, (long long)(unused - total), (long long)total); +free: + free(big_buffer); + return total; +} + +/** + * wipe_compressed_attribute - Wipe compressed $DATA attribute + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * @na: Opened ntfs attribute + * + * Return: >0 Success, the attribute was wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_compressed_attribute(ntfs_volume *vol, int byte, + enum action act, ntfs_attr *na) +{ + unsigned char *buf; + s64 size, offset, ret, wiped = 0; + le16 block_size_le; + u16 block_size; + VCN cur_vcn = 0; + runlist_element *rlc = na->rl; + s64 cu_mask = na->compression_block_clusters - 1; + runlist_element *restart = na->rl; + + while (rlc->length) { + cur_vcn += rlc->length; + if ((cur_vcn & cu_mask) || + (((rlc + 1)->length) && (rlc->lcn != LCN_HOLE))) { + rlc++; + continue; + } + + if (rlc->lcn == LCN_HOLE) { + runlist_element *rlt; + + offset = cur_vcn - rlc->length; + if (offset == (offset & (~cu_mask))) { + restart = rlc + 1; + rlc++; + continue; + } + offset = (offset & (~cu_mask)) + << vol->cluster_size_bits; + rlt = rlc; + while ((rlt - 1)->lcn == LCN_HOLE) rlt--; + while (1) { + ret = ntfs_rl_pread(vol, restart, + offset - (restart->vcn + << vol->cluster_size_bits), + 2, &block_size_le); + block_size = le16_to_cpu(block_size_le); + if (ret != 2) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("ntfs_rl_pread failed"); + return -1; + } + if (block_size == 0) { + offset += 2; + break; + } + block_size &= 0x0FFF; + block_size += 3; + offset += block_size; + if (offset >= (((rlt->vcn) << + vol->cluster_size_bits) - 2)) + goto next; + } + size = (rlt->vcn << vol->cluster_size_bits) - offset; + } else { + size = na->allocated_size - na->data_size; + offset = (cur_vcn << vol->cluster_size_bits) - size; + } + + if (size < 0) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("bug or damaged fs: we want " + "allocate buffer size %lld bytes", + (long long)size); + return -1; + } + + if ((act == act_info) || (!size)) { + wiped += size; + if (rlc->lcn == LCN_HOLE) + restart = rlc + 1; + rlc++; + continue; + } + + buf = malloc(size); + if (!buf) { + ntfs_log_verbose("Not enough memory\n"); + ntfs_log_error("Not enough memory to allocate " + "%lld bytes", + (long long)size); + return -1; + } + memset(buf, byte, size); + + ret = ntfs_rl_pwrite(vol, restart, + restart->vcn << vol->cluster_size_bits, + offset, size, buf); + free(buf); + if (ret != size) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("ntfs_rl_pwrite failed, offset %llu, " + "size %lld, vcn %lld", + (unsigned long long)offset, + (long long)size, (long long)rlc->vcn); + return -1; + } + wiped += ret; +next: + if (rlc->lcn == LCN_HOLE) + restart = rlc + 1; + rlc++; + } + + return wiped; +} + +/** + * wipe_attribute - Wipe not compressed $DATA attribute + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * @na: Opened ntfs attribute + * + * Return: >0 Success, the attribute was wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_attribute(ntfs_volume *vol, int byte, enum action act, + ntfs_attr *na) +{ + unsigned char *buf; + s64 wiped; + s64 size; + u64 offset = na->data_size; + + if (!offset) + return 0; + if (na->data_flags & ATTR_IS_ENCRYPTED) + offset = (((offset - 1) >> 10) + 1) << 10; + size = (vol->cluster_size - offset) % vol->cluster_size; + + if (act == act_info) + return size; + + buf = malloc(size); + if (!buf) { + ntfs_log_verbose("Not enough memory\n"); + ntfs_log_error("Not enough memory to allocate %lld bytes", + (long long)size); + return -1; + } + memset(buf, byte, size); + + wiped = ntfs_rl_pwrite(vol, na->rl, 0, offset, size, buf); + if (wiped == -1) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("Couldn't wipe tail"); + } + + free(buf); + return wiped; +} + +/* + * Wipe a data attribute tail + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ + +static s64 wipe_attr_tail(ntfs_inode *ni, ntfschar *name, int namelen, + int byte, enum action act) +{ + ntfs_attr *na; + ntfs_volume *vol = ni->vol; + s64 wiped; + + wiped = -1; + na = ntfs_attr_open(ni, AT_DATA, name, namelen); + if (!na) { + ntfs_log_error("Couldn't open $DATA attribute\n"); + goto close_attr; + } + + if (!NAttrNonResident(na)) { + ntfs_log_verbose("Resident $DATA attribute. Skipping.\n"); + goto close_attr; + } + + if (ntfs_attr_map_whole_runlist(na)) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("Can't map runlist (inode %lld)\n", + (long long)ni->mft_no); + goto close_attr; + } + + if (na->data_flags & ATTR_COMPRESSION_MASK) + wiped = wipe_compressed_attribute(vol, byte, act, na); + else + wiped = wipe_attribute(vol, byte, act, na); + + if (wiped == -1) { + ntfs_log_error(" (inode %lld)\n", (long long)ni->mft_no); + } + +close_attr: + ntfs_attr_close(na); + return (wiped); +} + +/** + * wipe_tails - Wipe the file tails in all its data attributes + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * Disk space is allocated in clusters. If a file isn't an exact multiple of + * the cluster size, there is some slack space at the end. Wipe this space. + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_tails(ntfs_volume *vol, int byte, enum action act) +{ + s64 total = 0; + s64 nr_mft_records, inode_num; + ntfs_attr_search_ctx *ctx; + ntfs_inode *ni; + ATTR_RECORD *a; + ntfschar *name; + + if (!vol || (byte < 0)) + return -1; + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + for (inode_num = FILE_first_user; inode_num < nr_mft_records; + inode_num++) { + s64 attr_wiped; + s64 wiped = 0; + + ntfs_log_verbose("Inode %lld - ", (long long)inode_num); + ni = ntfs_inode_open(vol, inode_num); + if (!ni) { + ntfs_log_verbose("Could not open inode\n"); + continue; + } + + if (ni->mrec->base_mft_record) { + ntfs_log_verbose("Not base mft record. Skipping\n"); + goto close_inode; + } + + ctx = ntfs_attr_get_search_ctx(ni, (MFT_RECORD*)NULL); + if (!ctx) { + ntfs_log_error("Can't get a context, aborting\n"); + ntfs_inode_close(ni); + goto close_abort; + } + while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, 0, + NULL, 0, ctx)) { + a = ctx->attr; + + if (!ctx->al_entry || !ctx->al_entry->lowest_vcn) { + name = (ntfschar*)((u8*)a + + le16_to_cpu(a->name_offset)); + attr_wiped = wipe_attr_tail(ni, name, + a->name_length, byte, act); + if (attr_wiped > 0) + wiped += attr_wiped; + } + } + ntfs_attr_put_search_ctx(ctx); + if (wiped) { + ntfs_log_verbose("Wiped %llu bytes\n", + (unsigned long long)wiped); + total += wiped; + } else + ntfs_log_verbose("Nothing to wipe\n"); +close_inode: + ntfs_inode_close(ni); + } +close_abort : + ntfs_log_quiet("wipe_tails 0x%02x, %lld bytes\n", byte, + (long long)total); + return total; +} + +/** + * wipe_mft - Wipe the MFT slack space + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * MFT Records are 1024 bytes long, but some of this space isn't used. Wipe any + * unused space at the end of the record and wipe any unused records. + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_mft(ntfs_volume *vol, int byte, enum action act) +{ + // by considering the individual attributes we might be able to + // wipe a few more bytes at the attr's tail. + s64 nr_mft_records, i; + s64 total = 0; + s64 result = 0; + int size = 0; + MFT_RECORD *rec = NULL; + + if (!vol || (byte < 0)) + return -1; + + rec = (MFT_RECORD*)malloc(vol->mft_record_size); + if (!rec) { + ntfs_log_error("malloc failed\n"); + return -1; + } + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + for (i = 0; i < nr_mft_records; i++) { + if (utils_mftrec_in_use(vol, i)) { + result = ntfs_attr_mst_pread(vol->mft_na, vol->mft_record_size * i, + 1, vol->mft_record_size, rec); + if (result != 1) { + ntfs_log_error("error attr mst read %lld\n", + (long long)i); + total = -1; // XXX just negate result? + goto free; + } + + // We know that the end marker will only take 4 bytes + size = le32_to_cpu(rec->bytes_in_use) - 4; + + if (act == act_info) { + //ntfs_log_info("mft %d\n", size); + total += size; + continue; + } + + memset(((u8*) rec) + size, byte, vol->mft_record_size - size); + } else { + const u16 usa_offset = + (vol->major_ver == 3) ? 0x0030 : 0x002A; + const u32 usa_size = 1 + + (vol->mft_record_size >> NTFS_BLOCK_SIZE_BITS); + const u16 attrs_offset = + ((usa_offset + usa_size) + 7) & ~((u16) 7); + const u32 bytes_in_use = attrs_offset + 8; + + if(usa_size > 0xFFFF || (usa_offset + usa_size) > + (NTFS_BLOCK_SIZE - sizeof(u16))) + { + ntfs_log_error("%d: usa_size out of bounds " + "(%u)\n", __LINE__, usa_size); + total = -1; + goto free; + } + + if (act == act_info) { + total += vol->mft_record_size; + continue; + } + + // Build the record from scratch + memset(rec, 0, vol->mft_record_size); + + // Common values + rec->magic = magic_FILE; + rec->usa_ofs = cpu_to_le16(usa_offset); + rec->usa_count = cpu_to_le16((u16) usa_size); + rec->sequence_number = cpu_to_le16(0x0001); + rec->attrs_offset = cpu_to_le16(attrs_offset); + rec->bytes_in_use = cpu_to_le32(bytes_in_use); + rec->bytes_allocated = cpu_to_le32(vol->mft_record_size); + rec->next_attr_instance = cpu_to_le16(0x0001); + + // End marker. + *((le32*) (((u8*) rec) + attrs_offset)) = cpu_to_le32(0xFFFFFFFF); + } + + result = ntfs_attr_mst_pwrite(vol->mft_na, vol->mft_record_size * i, + 1, vol->mft_record_size, rec); + if (result != 1) { + ntfs_log_error("error attr mst write %lld\n", + (long long)i); + total = -1; + goto free; + } + + if ((vol->mft_record_size * (i+1)) <= vol->mftmirr_na->allocated_size) + { + // We have to reduce the update sequence number, or else... + u16 offset; + le16 *usnp; + offset = le16_to_cpu(rec->usa_ofs); + usnp = (le16*) (((u8*) rec) + offset); + *usnp = cpu_to_le16(le16_to_cpu(*usnp) - 1); + + result = ntfs_attr_mst_pwrite(vol->mftmirr_na, vol->mft_record_size * i, + 1, vol->mft_record_size, rec); + if (result != 1) { + ntfs_log_error("error attr mst write %lld\n", + (long long)i); + total = -1; + goto free; + } + } + + total += vol->mft_record_size; + } + + ntfs_log_quiet("wipe_mft 0x%02x, %lld bytes\n", byte, (long long)total); +free: + free(rec); + return total; +} + +/** + * wipe_index_allocation - Wipe $INDEX_ALLOCATION attribute + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * @naa: Opened ntfs $INDEX_ALLOCATION attribute + * @nab: Opened ntfs $BITMAP attribute + * @indx_record_size: Size of INDX record + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_index_allocation(ntfs_volume *vol, int byte, enum action act + __attribute__((unused)), ntfs_attr *naa, ntfs_attr *nab, + u32 indx_record_size) +{ + s64 total = 0; + s64 wiped = 0; + s64 offset = 0; + s64 obyte = 0; + u64 wipe_offset; + s64 wipe_size; + u8 obit = 0; + u8 mask; + u8 *bitmap; + u8 *buf; + + bitmap = malloc(nab->data_size); + if (!bitmap) { + ntfs_log_verbose("malloc failed\n"); + ntfs_log_error("Couldn't allocate %lld bytes", + (long long)nab->data_size); + return -1; + } + + if (ntfs_attr_pread(nab, 0, nab->data_size, bitmap) + != nab->data_size) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("Couldn't read $BITMAP"); + total = -1; + goto free_bitmap; + } + + buf = malloc(indx_record_size); + if (!buf) { + ntfs_log_verbose("malloc failed\n"); + ntfs_log_error("Couldn't allocate %u bytes", + (unsigned int)indx_record_size); + total = -1; + goto free_bitmap; + } + + while (offset < naa->allocated_size) { + mask = 1 << obit; + if (bitmap[obyte] & mask) { + INDEX_ALLOCATION *indx; + + s64 ret = ntfs_rl_pread(vol, naa->rl, + offset, indx_record_size, buf); + if (ret != indx_record_size) { + ntfs_log_verbose("ntfs_rl_pread failed\n"); + ntfs_log_error("Couldn't read INDX record"); + total = -1; + goto free_buf; + } + + indx = (INDEX_ALLOCATION *) buf; + if (ntfs_mst_post_read_fixup((NTFS_RECORD *)buf, + indx_record_size)) + ntfs_log_error("damaged fs: mst_post_read_fixup failed"); + + if ((le32_to_cpu(indx->index.allocated_size) + 0x18) != + indx_record_size) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("INDX record should be %u bytes", + (unsigned int)indx_record_size); + total = -1; + goto free_buf; + } + + wipe_offset = le32_to_cpu(indx->index.index_length) + 0x18; + wipe_size = indx_record_size - wipe_offset; + memset(buf + wipe_offset, byte, wipe_size); + if (ntfs_mst_pre_write_fixup((NTFS_RECORD *)indx, + indx_record_size)) + ntfs_log_error("damaged fs: mst_pre_write_protect failed"); + if (opts.verbose > 1) + ntfs_log_verbose("+"); + } else { + wipe_size = indx_record_size; + memset(buf, byte, wipe_size); + if (opts.verbose > 1) + ntfs_log_verbose("x"); + } + + wiped = ntfs_rl_pwrite(vol, naa->rl, 0, offset, indx_record_size, buf); + if (wiped != indx_record_size) { + ntfs_log_verbose("ntfs_rl_pwrite failed\n"); + ntfs_log_error("Couldn't wipe tail of INDX record"); + total = -1; + goto free_buf; + } + total += wipe_size; + + offset += indx_record_size; + obit++; + if (obit > 7) { + obit = 0; + obyte++; + } + } + if ((opts.verbose > 1) && (wiped != -1)) + ntfs_log_verbose("\n\t"); +free_buf: + free(buf); +free_bitmap: + free(bitmap); + return total; +} + +/** + * get_indx_record_size - determine size of INDX record from $INDEX_ROOT + * @nar: Opened ntfs $INDEX_ROOT attribute + * + * Return: >0 Success, return INDX record size + * 0 Error, something went wrong + */ +static u32 get_indx_record_size(ntfs_attr *nar) +{ + u32 indx_record_size; + le32 indx_record_size_le; + + if (ntfs_attr_pread(nar, 8, 4, &indx_record_size_le) != 4) { + ntfs_log_verbose("Couldn't determine size of INDX record\n"); + ntfs_log_error("ntfs_attr_pread failed"); + return 0; + } + + indx_record_size = le32_to_cpu(indx_record_size_le); + if (!indx_record_size) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("INDX record should be 0"); + } + return indx_record_size; +} + +/** + * wipe_directory - Wipe the directory indexes + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * Directories are kept in sorted B+ Trees. Index blocks may not be full. Wipe + * the unused space at the ends of these blocks. + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_directory(ntfs_volume *vol, int byte, enum action act) +{ + s64 total = 0; + s64 nr_mft_records, inode_num; + ntfs_inode *ni; + ntfs_attr *naa; + ntfs_attr *nab; + ntfs_attr *nar; + + if (!vol || (byte < 0)) + return -1; + + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + for (inode_num = 5; inode_num < nr_mft_records; inode_num++) { + u32 indx_record_size; + s64 wiped; + + ntfs_log_verbose("Inode %lld - ", (long long)inode_num); + ni = ntfs_inode_open(vol, inode_num); + if (!ni) { + if (opts.verbose > 2) + ntfs_log_verbose("Could not open inode\n"); + else + ntfs_log_verbose("\r"); + continue; + } + + if (ni->mrec->base_mft_record) { + if (opts.verbose > 2) + ntfs_log_verbose("Not base mft record. Skipping\n"); + else + ntfs_log_verbose("\r"); + goto close_inode; + } + + naa = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (!naa) { + if (opts.verbose > 2) + ntfs_log_verbose("Couldn't open $INDEX_ALLOCATION\n"); + else + ntfs_log_verbose("\r"); + goto close_inode; + } + + if (!NAttrNonResident(naa)) { + ntfs_log_verbose("Resident $INDEX_ALLOCATION\n"); + ntfs_log_error("damaged fs: Resident $INDEX_ALLOCATION " + "(inode %lld)\n", (long long)inode_num); + goto close_attr_allocation; + } + + if (ntfs_attr_map_whole_runlist(naa)) { + ntfs_log_verbose("Internal error\n"); + ntfs_log_error("Can't map runlist for $INDEX_ALLOCATION " + "(inode %lld)\n", (long long)inode_num); + goto close_attr_allocation; + } + + nab = ntfs_attr_open(ni, AT_BITMAP, NTFS_INDEX_I30, 4); + if (!nab) { + ntfs_log_verbose("Couldn't open $BITMAP\n"); + ntfs_log_error("damaged fs: $INDEX_ALLOCATION is present, " + "but we can't open $BITMAP with same " + "name (inode %lld)\n", (long long)inode_num); + goto close_attr_allocation; + } + + nar = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4); + if (!nar) { + ntfs_log_verbose("Couldn't open $INDEX_ROOT\n"); + ntfs_log_error("damaged fs: $INDEX_ALLOCATION is present, but " + "we can't open $INDEX_ROOT with same name" + " (inode %lld)\n", (long long)inode_num); + goto close_attr_bitmap; + } + + if (NAttrNonResident(nar)) { + ntfs_log_verbose("Not resident $INDEX_ROOT\n"); + ntfs_log_error("damaged fs: Not resident $INDEX_ROOT " + "(inode %lld)\n", (long long)inode_num); + goto close_attr_root; + } + + indx_record_size = get_indx_record_size(nar); + if (!indx_record_size) { + ntfs_log_error(" (inode %lld)\n", (long long)inode_num); + goto close_attr_root; + } + + wiped = wipe_index_allocation(vol, byte, act, + naa, nab, indx_record_size); + if (wiped == -1) { + ntfs_log_error(" (inode %lld)\n", + (long long)inode_num); + goto close_attr_root; + } + + if (wiped) { + ntfs_log_verbose("Wiped %llu bytes\n", + (unsigned long long)wiped); + total += wiped; + } else + ntfs_log_verbose("Nothing to wipe\n"); +close_attr_root: + ntfs_attr_close(nar); +close_attr_bitmap: + ntfs_attr_close(nab); +close_attr_allocation: + ntfs_attr_close(naa); +close_inode: + ntfs_inode_close(ni); + } + + ntfs_log_quiet("wipe_directory 0x%02x, %lld bytes\n", byte, + (long long)total); + return total; +} + +/** + * wipe_logfile - Wipe the logfile (journal) + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * The logfile journals the metadata to give the volume fault-tolerance. If the + * volume is in a consistent state, then this information can be erased. + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_logfile(ntfs_volume *vol, int byte, enum action act + __attribute__((unused))) +{ + const int NTFS_BUF_SIZE2 = 8192; + //FIXME(?): We might need to zero the LSN field of every single mft + //record as well. (But, first try without doing that and see what + //happens, since chkdsk might pickup the pieces and do it for us...) + ntfs_inode *ni; + ntfs_attr *na; + s64 len, pos, count; + char buf[NTFS_BUF_SIZE2]; + int eo; + + /* We can wipe logfile only with 0xff. */ + byte = 0xff; + + if (!vol || (byte < 0)) + return -1; + + //ntfs_log_quiet("wipe_logfile(not implemented) 0x%02x\n", byte); + + if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) { + ntfs_log_debug("Failed to open inode FILE_LogFile.\n"); + return -1; + } + + if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) { + ntfs_log_debug("Failed to open $FILE_LogFile/$DATA.\n"); + goto error_exit; + } + + /* The $DATA attribute of the $LogFile has to be non-resident. */ + if (!NAttrNonResident(na)) { + ntfs_log_debug("$LogFile $DATA attribute is resident!?!\n"); + errno = EIO; + goto io_error_exit; + } + + /* Get length of $LogFile contents. */ + len = na->data_size; + if (!len) { + ntfs_log_debug("$LogFile has zero length, no disk write " + "needed.\n"); + return 0; + } + + /* Read $LogFile until its end. We do this as a check for correct + length thus making sure we are decompressing the mapping pairs + array correctly and hence writing below is safe as well. */ + pos = 0; + while ((count = ntfs_attr_pread(na, pos, NTFS_BUF_SIZE2, buf)) > 0) + pos += count; + + if (count == -1 || pos != len) { + ntfs_log_debug("Amount of $LogFile data read does not " + "correspond to expected length!\n"); + if (count != -1) + errno = EIO; + goto io_error_exit; + } + + /* Fill the buffer with @byte's. */ + memset(buf, byte, NTFS_BUF_SIZE2); + + /* Set the $DATA attribute. */ + pos = 0; + while ((count = len - pos) > 0) { + if (count > NTFS_BUF_SIZE2) + count = NTFS_BUF_SIZE2; + + if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) { + ntfs_log_debug("Failed to set the $LogFile attribute " + "value.\n"); + if (count != -1) + errno = EIO; + goto io_error_exit; + } + + pos += count; + } + + ntfs_attr_close(na); + ntfs_inode_close(ni); + ntfs_log_quiet("wipe_logfile 0x%02x, %lld bytes\n", byte, + (long long)pos); + return pos; + +io_error_exit: + eo = errno; + ntfs_attr_close(na); + errno = eo; +error_exit: + eo = errno; + ntfs_inode_close(ni); + errno = eo; + return -1; +} + +/** + * wipe_pagefile - Wipe the pagefile (swap space) + * @vol: An ntfs volume obtained from ntfs_mount + * @byte: Overwrite with this value + * @act: Wipe, test or info + * + * pagefile.sys is used by Windows as extra virtual memory (swap space). + * Windows recreates the file at bootup, so it can be wiped without harm. + * + * Return: >0 Success, the clusters were wiped + * 0 Nothing to wipe + * -1 Error, something went wrong + */ +static s64 wipe_pagefile(ntfs_volume *vol, int byte, enum action act + __attribute__((unused))) +{ + // wipe completely, chkdsk doesn't do anything, booting writes header + const int NTFS_BUF_SIZE2 = 4096; + ntfs_inode *ni; + ntfs_attr *na; + s64 len, pos, count; + char buf[NTFS_BUF_SIZE2]; + int eo; + + if (!vol || (byte < 0)) + return -1; + + //ntfs_log_quiet("wipe_pagefile(not implemented) 0x%02x\n", byte); + + ni = ntfs_pathname_to_inode(vol, NULL, "pagefile.sys"); + if (!ni) { + ntfs_log_debug("Failed to open inode of pagefile.sys.\n"); + return 0; + } + + if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) { + ntfs_log_debug("Failed to open pagefile.sys/$DATA.\n"); + goto error_exit; + } + + /* The $DATA attribute of the pagefile.sys has to be non-resident. */ + if (!NAttrNonResident(na)) { + ntfs_log_debug("pagefile.sys $DATA attribute is resident!?!\n"); + errno = EIO; + goto io_error_exit; + } + + /* Get length of pagefile.sys contents. */ + len = na->data_size; + if (!len) { + ntfs_log_debug("pagefile.sys has zero length, no disk write " + "needed.\n"); + return 0; + } + + memset(buf, byte, NTFS_BUF_SIZE2); + + /* Set the $DATA attribute. */ + pos = 0; + while ((count = len - pos) > 0) { + if (count > NTFS_BUF_SIZE2) + count = NTFS_BUF_SIZE2; + + if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) { + ntfs_log_debug("Failed to set the pagefile.sys " + "attribute value.\n"); + if (count != -1) + errno = EIO; + goto io_error_exit; + } + + pos += count; + } + + ntfs_attr_close(na); + ntfs_inode_close(ni); + ntfs_log_quiet("wipe_pagefile 0x%02x, %lld bytes\n", byte, + (long long)pos); + return pos; + +io_error_exit: + eo = errno; + ntfs_attr_close(na); + errno = eo; +error_exit: + eo = errno; + ntfs_inode_close(ni); + errno = eo; + return -1; +} + +/** + * Part of ntfsprogs. + * Modified: removed logging, signal handling, removed data. + * + * free_file - Release the resources used by a file object + * \param file The unwanted file object + * + * This will free up the memory used by a file object and iterate through the + * object's children, freeing their resources too. + * + * \return none + */ +static void free_file (struct ufile *file) +{ + struct ntfs_list_head *item = NULL, *tmp = NULL; + struct filename *f = NULL; + struct data *d = NULL; + + if (file == NULL) + return; + + ntfs_list_for_each_safe(item, tmp, &(file->name)) { + /* List of filenames */ + + f = ntfs_list_entry(item, struct filename, list); + if (f->name != NULL) + free(f->name); + if (f->parent_name != NULL) { + free(f->parent_name); + } + free(f); + } + + ntfs_list_for_each_safe(item, tmp, &(file->data)) { + /* List of data streams */ + + d = ntfs_list_entry(item, struct data, list); + if (d->name != NULL) + free(d->name); + if (d->runlist != NULL) + free(d->runlist); + free(d); + } + + + free(file->mft); + free(file); +} + +/** + * Fills the given buffer with one of predefined patterns. + * \param pat_no Pass number. + * \param buffer Buffer to be filled. + * \param buflen Length of the buffer. + */ +static void fill_buffer ( + unsigned long int pat_no, + unsigned char * const buffer, + const size_t buflen, + int * const selected ) + /*@requires notnull buffer @*/ /*@sets *buffer @*/ +{ + + size_t i; +#if (!defined HAVE_MEMCPY) && (!defined HAVE_STRING_H) + size_t j; +#endif + unsigned int bits; + + if ((buffer == NULL) || (buflen == 0)) + return; + + /* De-select all patterns once every npasses calls. */ + if (pat_no % npasses == 0) { + for (i = 0; i < NPAT; i++) { + selected[i] = 0; + } + } + pat_no %= npasses; + /* double check for npasses >= NPAT + 3: */ + for (i = 0; i < NPAT; i++) { + if (selected[i] == 0) + break; + } + if (i >= NPAT) { + for (i = 0; i < NPAT; i++) { + selected[i] = 0; + } + } + + /* The first, last and middle passess will be using a random pattern */ + if ((pat_no == 0) || (pat_no == npasses-1) || (pat_no == npasses/2)) { +#if (!defined __STRICT_ANSI__) && (defined HAVE_RANDOM) + bits = (unsigned int)(random() & 0xFFF); +#else + bits = (unsigned int)(rand() & 0xFFF); +#endif + } else { + /* For other passes, one of the fixed patterns is selected. */ + do { +#if (!defined __STRICT_ANSI__) && (defined HAVE_RANDOM) + i = (size_t)(random() % NPAT); +#else + i = (size_t)(rand() % NPAT); +#endif + } while (selected[i] == 1); + bits = opts.bytes[i]; + selected[i] = 1; + } + + buffer[0] = (unsigned char) bits; + buffer[1] = (unsigned char) bits; + buffer[2] = (unsigned char) bits; + for (i = 3; i < buflen / 2; i *= 2) { +#ifdef HAVE_MEMCPY + memcpy(buffer + i, buffer, i); +#elif defined HAVE_STRING_H + strncpy((char *)(buffer + i), (char *)buffer, i); +#else + for (j = 0; j < i; j++) { + buffer[i+j] = buffer[j]; + } +#endif + } + if (i < buflen) { +#ifdef HAVE_MEMCPY + memcpy(buffer + i, buffer, buflen - i); +#elif defined HAVE_STRING_H + strncpy((char *)(buffer + i), (char *)buffer, buflen - i); +#else + for (j=0; j<buflen - i; j++) { + buffer[i+j] = buffer[j]; + } +#endif + } +} + +/** + * Destroys the specified record's filenames and data. + * + * \param nv The filesystem. + * \param record The record (i-node number), which filenames & data + * to destroy. + * \return 0 in case of no errors, other values otherwise. + */ +static int destroy_record(ntfs_volume *nv, const s64 record, + unsigned char * const buf) +{ + struct ufile *file = NULL; + runlist_element *rl = NULL; + ntfs_attr *mft = NULL; + + ntfs_attr_search_ctx *ctx = NULL; + int ret_wfs = 0; + unsigned long int pass, i; + s64 j; + unsigned char * a_offset; + int selected[NPAT]; + + file = (struct ufile *) malloc(sizeof(struct ufile)); + if (file == NULL) { + return -1; + } + + NTFS_INIT_LIST_HEAD(&(file->name)); + NTFS_INIT_LIST_HEAD(&(file->data)); + file->inode = record; + + file->mft = (MFT_RECORD*)malloc(nv->mft_record_size); + if (file->mft == NULL) { + free_file (file); + return -1; + } + + mft = ntfs_attr_open(nv->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (mft == NULL) { + free_file(file); + return -2; + } + + /* Read the MFT reocrd of the i-node */ + if (ntfs_attr_mst_pread(mft, nv->mft_record_size * record, 1LL, + nv->mft_record_size, file->mft) < 1) { + + ntfs_attr_close(mft); + free_file(file); + return -3; + } + ntfs_attr_close(mft); + mft = NULL; + + ctx = ntfs_attr_get_search_ctx(NULL, file->mft); + if (ctx == NULL) { + free_file(file); + return -4; + } + + /* Wiping file names */ + while (1 == 1) { + + if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, + 0LL, NULL, 0, ctx) != 0) { + break; /* None / no more of that type */ + } + if (ctx->attr == NULL) + break; + + /* We know this will always be resident. + Find the offset of the data, including the MFT record. */ + a_offset = ((unsigned char *) ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + for (pass = 0; pass < npasses; pass++) { + fill_buffer(pass, a_offset, + le32_to_cpu(ctx->attr->value_length), + selected); + + if ( !opts.noaction ) { + if (ntfs_mft_records_write(nv, + MK_MREF(record, 0), 1LL, + ctx->mrec) != 0) { + ret_wfs = -5; + break; + } + /* Flush after each writing, if more than + 1 overwriting needs to be done. Allow I/O + bufferring (efficiency), if just one + pass is needed. */ + if (npasses > 1) { + nv->dev->d_ops->sync(nv->dev); + } + } + + } + + /* Wiping file name length */ + for (pass = 0; pass < npasses; pass++) { + + fill_buffer (pass, (unsigned char *) + &(ctx->attr->value_length), sizeof(u32), + selected); + + if (!opts.noaction) { + if (ntfs_mft_records_write(nv, + MK_MREF(record, 0), + 1LL, ctx->mrec) != 0) { + ret_wfs = -5; + break; + } + + if (npasses > 1) { + nv->dev->d_ops->sync(nv->dev); + } + } + } + ctx->attr->value_length = cpu_to_le32(0); + if (!opts.noaction) { + if (ntfs_mft_records_write(nv, MK_MREF(record, 0), + 1LL, ctx->mrec) != 0) { + ret_wfs = -5; + break; + } + } + } + + ntfs_attr_reinit_search_ctx(ctx); + + /* Wiping file data */ + while (1 == 1) { + if (ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, 0LL, + NULL, 0, ctx) != 0) { + break; /* None / no more of that type */ + } + if (ctx->attr == NULL) + break; + + if (ctx->attr->non_resident == 0) { + /* attribute is resident (part of MFT record) */ + /* find the offset of the data, including the MFT record */ + a_offset = ((unsigned char *) ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + /* Wiping the data itself */ + for (pass = 0; pass < npasses; pass++) { + + fill_buffer (pass, a_offset, + le32_to_cpu(ctx->attr->value_length), + selected); + + if (!opts.noaction) { + if (ntfs_mft_records_write(nv, + MK_MREF(record, 0), + 1LL, ctx->mrec) != 0) { + ret_wfs = -5; + break; + } + + if (npasses > 1) { + nv->dev->d_ops->sync(nv->dev); + } + } + } + + /* Wiping data length */ + for (pass = 0; pass < npasses; pass++) { + + fill_buffer(pass, (unsigned char *) + &(ctx->attr->value_length), + sizeof(u32), selected); + + if (!opts.noaction) { + if (ntfs_mft_records_write(nv, + MK_MREF(record, 0), + 1LL, ctx->mrec) != 0) { + ret_wfs = -5; + break; + } + + if (npasses > 1) { + nv->dev->d_ops->sync(nv->dev); + } + } + } + ctx->attr->value_length = cpu_to_le32(0); + if ( !opts.noaction ) { + if (ntfs_mft_records_write(nv, + MK_MREF(record, 0), + 1LL, ctx->mrec) != 0) { + ret_wfs = -5; + break; + } + } + } else { + /* Non-resident here */ + + rl = ntfs_mapping_pairs_decompress(nv, + ctx->attr, NULL); + if (rl == NULL) { + continue; + } + + if (rl[0].length <= 0) { + continue; + } + + for (i = 0; (rl[i].length > 0) && (ret_wfs == 0); i++) { + if (rl[i].lcn == -1) { + continue; + } + for (j = rl[i].lcn; + (j < rl[i].lcn + rl[i].length) + && (ret_wfs == 0); j++) { + + if (utils_cluster_in_use(nv, j) != 0) + continue; + for (pass = 0; + pass < npasses; + pass++) { + + fill_buffer(pass, buf, + (size_t) nv->cluster_size, + selected); + if (!opts.noaction) { + if (ntfs_cluster_write( + nv, j, 1LL, + buf) < 1) { + ret_wfs = -5; + break; + } + + if (npasses > 1) { + nv->dev->d_ops->sync + (nv->dev); + } + } + } + } + } + + /* Wipe the data length here */ + for (pass = 0; pass < npasses; pass++) { + fill_buffer(pass, (unsigned char *) + &(ctx->attr->lowest_vcn), + sizeof(VCN), selected); + fill_buffer(pass, (unsigned char *) + &(ctx->attr->highest_vcn), + sizeof(VCN), selected); + fill_buffer(pass, (unsigned char *) + &(ctx->attr->allocated_size), + sizeof(s64), selected); + fill_buffer(pass, (unsigned char *) + &(ctx->attr->data_size), + sizeof(s64), selected); + fill_buffer(pass, (unsigned char *) + &(ctx->attr->initialized_size), + sizeof(s64), selected); + fill_buffer(pass, (unsigned char *) + &(ctx->attr->compressed_size), + sizeof(s64), selected); + + if ( !opts.noaction ) { + if (ntfs_mft_records_write(nv, + MK_MREF (record, 0), + 1LL, ctx->mrec) != 0) { + ret_wfs = -5; + break; + } + + if (npasses > 1) { + nv->dev->d_ops->sync(nv->dev); + } + } + } + ctx->attr->lowest_vcn = cpu_to_le64(0); + ctx->attr->highest_vcn = cpu_to_le64(0); + ctx->attr->allocated_size = cpu_to_le64(0); + ctx->attr->data_size = cpu_to_le64(0); + ctx->attr->initialized_size = cpu_to_le64(0); + ctx->attr->compressed_size = cpu_to_le64(0); + if (!opts.noaction) { + if (ntfs_mft_records_write(nv, + MK_MREF (record, 0), + 1LL, ctx->mrec) != 0) { + ret_wfs = -5; + break; + } + } + } /* end of resident check */ + } /* end of 'wiping file data' loop */ + + ntfs_attr_put_search_ctx(ctx); + free_file(file); + + return ret_wfs; +} + +/** + * Starts search for deleted inodes and undelete data on the given + * NTFS filesystem. + * \param FS The filesystem. + * \return 0 in case of no errors, other values otherwise. + */ +static int wipe_unrm(ntfs_volume *nv) +{ + int ret_wfs = 0, ret; + ntfs_attr *bitmapattr = NULL; + s64 bmpsize, size, nr_mft_records, i, j, k; + unsigned char b; + unsigned char * buf = NULL; + +#define MYBUF_SIZE 8192 + unsigned char *mybuf; +#define MINIM(x, y) ( ((x)<(y))?(x):(y) ) + + mybuf = (unsigned char *) malloc(MYBUF_SIZE); + if (mybuf == NULL) { + return -1; + } + + buf = (unsigned char *) malloc(nv->cluster_size); + if (buf == NULL) { + free (mybuf); + return -1; + } + + bitmapattr = ntfs_attr_open(nv->mft_ni, AT_BITMAP, AT_UNNAMED, 0); + if (bitmapattr == NULL) { + free (buf); + free (mybuf); + return -2; + } + bmpsize = bitmapattr->initialized_size; + + nr_mft_records = nv->mft_na->initialized_size + >> nv->mft_record_size_bits; + + /* just like ntfsundelete; detects i-node numbers fine */ + for (i = 0; (i < bmpsize) && (ret_wfs==0); i += MYBUF_SIZE) { + + /* read a part of the file bitmap */ + size = ntfs_attr_pread(bitmapattr, i, + MINIM((bmpsize - i), MYBUF_SIZE), mybuf); + if (size < 0) + break; + + /* parse each byte of the just-read part of the bitmap */ + for (j = 0; (j < size) && (ret_wfs==0); j++) { + b = mybuf[j]; + /* parse each bit of the byte Bit 1 means 'in use'. */ + for (k = 0; (k < CHAR_BIT) && (ret_wfs==0); + k++, b>>=1) { + /* (i+j)*8+k is the i-node bit number */ + if (((i+j)*CHAR_BIT+k) >= nr_mft_records) { + goto done; + } + if ((b & 1) != 0) { + /* i-node is in use, skip it */ + continue; + } + /* wiping the i-node here: */ + ret = destroy_record (nv, + (i+j)*CHAR_BIT+k, buf); + if (ret != 0) { + ret_wfs = ret; + } + } + } + } +done: + ntfs_attr_close(bitmapattr); + free(buf); + free(mybuf); + + ntfs_log_quiet("wipe_undelete\n"); + return ret_wfs; +} + + + +/** + * print_summary - Tell the user what we are about to do + * + * List the operations about to be performed. The output will be silenced by + * the --quiet option. + * + * Return: none + */ +static void print_summary(void) +{ + int i; + + if (opts.noaction) + ntfs_log_quiet("%s is in 'no-action' mode, it will NOT write to disk." + "\n\n", EXEC_NAME); + + ntfs_log_quiet("%s is about to wipe:\n", EXEC_NAME); + if (opts.unused) + ntfs_log_quiet("\tunused disk space\n"); + if (opts.unused_fast) + ntfs_log_quiet("\tunused disk space (fast)\n"); + if (opts.tails) + ntfs_log_quiet("\tfile tails\n"); + if (opts.mft) + ntfs_log_quiet("\tunused mft areas\n"); + if (opts.directory) + ntfs_log_quiet("\tunused directory index space\n"); + if (opts.logfile) + ntfs_log_quiet("\tthe logfile (journal)\n"); + if (opts.pagefile) + ntfs_log_quiet("\tthe pagefile (swap space)\n"); + if (opts.undel) + ntfs_log_quiet("\tundelete data\n"); + + ntfs_log_quiet("\n%s will overwrite these areas with: ", EXEC_NAME); + if (opts.bytes) { + for (i = 0; opts.bytes[i] >= 0; i++) + ntfs_log_quiet("0x%02x ", opts.bytes[i]); + } + ntfs_log_quiet("\n"); + + if (opts.count > 1) + ntfs_log_quiet("%s will repeat these operations %d times.\n", EXEC_NAME, opts.count); + ntfs_log_quiet("\n"); +} + +/** + * main - Begin here + * + * Start from here. + * + * Return: 0 Success, the program worked + * 1 Error, something went wrong + */ +int main(int argc, char *argv[]) +{ + ntfs_volume *vol; + int result = 1; + int flags = 0; + int res; + int i, j; + enum action act = act_info; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + + res = parse_options(argc, argv); + if (res >= 0) + return (res); + + utils_set_locale(); + + if (!opts.info) + print_summary(); + + if (opts.info || opts.noaction) + flags = NTFS_MNT_RDONLY; + if (opts.force) + flags |= NTFS_MNT_RECOVER; + + vol = utils_mount_volume(opts.device, flags); + if (!vol) + goto free; + + if ((vol->flags & VOLUME_IS_DIRTY) && !opts.force) + goto umount; + + if (opts.info) { + act = act_info; + opts.count = 1; + } else if (opts.noaction) { + act = act_test; + } else { + act = act_wipe; + } + + /* Even if the output it quieted, you still get 5 seconds to abort. */ + if ((act == act_wipe) && !opts.force) { + ntfs_log_quiet("\n%s will begin in 5 seconds, press CTRL-C to abort.\n", EXEC_NAME); + sleep(5); + } + + for (i = 0; opts.bytes[i] >= 0; i++) { + npasses = i+1; + } + if (npasses == 0) { + npasses = opts.count; + } +#ifdef HAVE_TIME_H + srandom(time(NULL)); +#else + /* use a pointer as a pseudorandom value */ + srandom((int)vol + npasses); +#endif + ntfs_log_info("\n"); + for (i = 0; i < opts.count; i++) { + int byte; + s64 total = 0; + s64 wiped = 0; + + for (j = 0; byte = opts.bytes[j], byte >= 0; j++) { + + if (opts.directory) { + wiped = wipe_directory(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (opts.tails) { + wiped = wipe_tails(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (opts.logfile) { + wiped = wipe_logfile(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (opts.mft) { + wiped = wipe_mft(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (opts.pagefile) { + wiped = wipe_pagefile(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (opts.unused || opts.unused_fast) { + if (opts.unused_fast) + wiped = wipe_unused_fast(vol, byte, + act); + else + wiped = wipe_unused(vol, byte, act); + if (wiped < 0) + goto umount; + else + total += wiped; + } + + if (opts.undel) { + wiped = wipe_unrm(vol); + if (wiped != 0) + goto umount; + /* + else + total += wiped; + */ + } + + if (act == act_info) + break; + } + + ntfs_log_info( + "%lld bytes were wiped (excluding undelete data)\n", + (long long)total); + } + result = 0; +umount: + ntfs_umount(vol, FALSE); +free: + if (opts.bytes) + free(opts.bytes); + return result; +} diff --git a/ntfsprogs/ntfswipe.h b/ntfsprogs/ntfswipe.h new file mode 100755 index 0000000000000000000000000000000000000000..08620e5ffbc706dd02a10fe43423cf49696cbc99 --- /dev/null +++ b/ntfsprogs/ntfswipe.h @@ -0,0 +1,55 @@ +/* + * ntfswipe - Part of the Linux-NTFS project. + * + * Copyright (c) 2002 Richard Russon + * + * This utility will overwrite unused space on an NTFS volume. + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFSWIPE_H_ +#define _NTFSWIPE_H_ + +#include "types.h" + +enum action { + act_info, + act_test, + act_wipe, +}; + +struct options { + char *device; /* Device/File to work with */ + int info; /* Show volume info */ + int force; /* Override common sense */ + int quiet; /* Less output */ + int verbose; /* Extra output */ + int noaction; /* Do not write to disk */ + int count; /* Number of iterations */ + int *bytes; /* List of overwrite characters */ + int directory; /* Wipe directory indexes */ + int logfile; /* Wipe the logfile (journal) */ + int mft; /* Wipe mft slack space */ + int pagefile; /* Wipe pagefile (swap space) */ + int tails; /* Wipe file tails */ + int unused; /* Wipe unused clusters */ + int unused_fast; /* Wipe unused clusters (fast) */ + int undel; /* Wipe undelete data */ +}; + +#endif /* _NTFSWIPE_H_ */ + diff --git a/ntfsprogs/sd.c b/ntfsprogs/sd.c new file mode 100755 index 0000000000000000000000000000000000000000..4e3af97801bf5df8c93ea9d643d3ab8719f99eb1 --- /dev/null +++ b/ntfsprogs/sd.c @@ -0,0 +1,607 @@ +#include "types.h" +#include "layout.h" +#include "sd.h" + +/** + * init_system_file_sd - + * + * NTFS 3.1 - System files security decriptors + * ===================================================== + * + * Create the security descriptor for system file number @sys_file_no and + * return a pointer to the descriptor. + * + * Note the root directory system file (".") is very different and handled by a + * different function. + * + * The sd is returned in *@sd_val and has length *@sd_val_len. + * + * Do NOT free *@sd_val as it is static memory. This also means that you can + * only use *@sd_val until the next call to this function. + */ +void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len) +{ + static u8 sd_array[0x68]; + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *aa_ace; + SID *sid; + le32 *sub_authorities; + + if (sys_file_no < 0) { + *sd_val = NULL; + *sd_val_len = 0; + return; + } + *sd_val = sd_array; + sd = (SECURITY_DESCRIPTOR_RELATIVE*)&sd_array; + sd->revision = 1; + sd->alignment = 0; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + *sd_val_len = 0x64; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x54); + sd->sacl = const_cpu_to_le32(0); + sd->dacl = const_cpu_to_le32(0x14); + /* + * Now at offset 0x14, as specified in the security descriptor, we have + * the DACL. + */ + acl = (ACL*)((char*)sd + le32_to_cpu(sd->dacl)); + acl->revision = 2; + acl->alignment1 = 0; + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(2); + acl->alignment2 = const_cpu_to_le16(0); + /* + * Now at offset 0x1c, just after the DACL's ACL, we have the first + * ACE of the DACL. The type of the ACE is access allowed. + */ + aa_ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + aa_ace->type = ACCESS_ALLOWED_ACE_TYPE; + aa_ace->flags = 0; + aa_ace->size = const_cpu_to_le16(0x14); + switch (sys_file_no) { + case FILE_AttrDef: + case FILE_Boot: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | + FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_READ_DATA; + break; + default: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_WRITE | + FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | + FILE_WRITE_EA | FILE_READ_EA | FILE_APPEND_DATA | + FILE_WRITE_DATA | FILE_READ_DATA; + break; + } + aa_ace->sid.revision = 1; + aa_ace->sid.sub_authority_count = 1; + aa_ace->sid.identifier_authority.value[0] = 0; + aa_ace->sid.identifier_authority.value[1] = 0; + aa_ace->sid.identifier_authority.value[2] = 0; + aa_ace->sid.identifier_authority.value[3] = 0; + aa_ace->sid.identifier_authority.value[4] = 0; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + aa_ace->sid.identifier_authority.value[5] = 5; + aa_ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + /* + * Now at offset 0x30 within security descriptor, just after the first + * ACE of the DACL. All system files, except the root directory, have + * a second ACE. + */ + /* The second ACE of the DACL. Type is access allowed. */ + aa_ace = (ACCESS_ALLOWED_ACE*)((char*)aa_ace + + le16_to_cpu(aa_ace->size)); + aa_ace->type = ACCESS_ALLOWED_ACE_TYPE; + aa_ace->flags = 0; + aa_ace->size = const_cpu_to_le16(0x18); + /* Only $AttrDef and $Boot behave differently to everything else. */ + switch (sys_file_no) { + case FILE_AttrDef: + case FILE_Boot: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | + FILE_READ_ATTRIBUTES | FILE_READ_EA | + FILE_READ_DATA; + break; + default: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | + FILE_WRITE_ATTRIBUTES | + FILE_READ_ATTRIBUTES | FILE_WRITE_EA | + FILE_READ_EA | FILE_APPEND_DATA | + FILE_WRITE_DATA | FILE_READ_DATA; + break; + } + aa_ace->sid.revision = 1; + aa_ace->sid.sub_authority_count = 2; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + aa_ace->sid.identifier_authority.value[0] = 0; + aa_ace->sid.identifier_authority.value[1] = 0; + aa_ace->sid.identifier_authority.value[2] = 0; + aa_ace->sid.identifier_authority.value[3] = 0; + aa_ace->sid.identifier_authority.value[4] = 0; + aa_ace->sid.identifier_authority.value[5] = 5; + sub_authorities = aa_ace->sid.sub_authority; + *sub_authorities++ = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + /* + * Now at offset 0x48 into the security descriptor, as specified in the + * security descriptor, we now have the owner SID. + */ + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 1; + sid->sub_authority_count = 1; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + /* + * Now at offset 0x54 into the security descriptor, as specified in the + * security descriptor, we have the group SID. + */ + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 1; + sid->sub_authority_count = 2; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sub_authorities = sid->sub_authority; + *sub_authorities++ = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); +} + +/** + * init_root_sd - + * + * Creates the security_descriptor for the root folder on ntfs 3.1 as created + * by Windows Vista (when the format is done from the disk management MMC + * snap-in, note this is different from the format done from the disk + * properties in Windows Explorer). + */ +void init_root_sd(u8 **sd_val, int *sd_val_len) +{ + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *ace; + SID *sid; + le32 *sub_authorities; + + static char sd_array[0x102c]; + *sd_val_len = 0x102c; + *sd_val = (u8*)&sd_array; + + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)sd_array; + sd->revision = SECURITY_DESCRIPTOR_REVISION; + sd->alignment = 0; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x1014); + sd->group = const_cpu_to_le32(0x1020); + sd->sacl = 0; + sd->dacl = const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + + //acl + acl = (ACL*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = ACL_REVISION; + acl->alignment1 = 0; + acl->size = const_cpu_to_le16(0x1000); + acl->ace_count = const_cpu_to_le16(0x08); + acl->alignment2 = 0; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; + ace->size = const_cpu_to_le16(0x18); + ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | FILE_WRITE_DATA | + FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | + FILE_TRAVERSE | FILE_DELETE_CHILD | + FILE_READ_ATTRIBUTES; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + sub_authorities = ace->sid.sub_authority; + *sub_authorities++ = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x18); + ace->mask = GENERIC_ALL; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + sub_authorities = ace->sid.sub_authority; + *sub_authorities++ = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //ace3 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; + ace->size = const_cpu_to_le16(0x14); + ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | FILE_WRITE_DATA | + FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | + FILE_TRAVERSE | FILE_DELETE_CHILD | + FILE_READ_ATTRIBUTES; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + //ace4 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x14); + ace->mask = GENERIC_ALL; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + //ace5 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; + ace->size = const_cpu_to_le16(0x14); + ace->mask = SYNCHRONIZE | READ_CONTROL | DELETE | + FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | + FILE_TRAVERSE | FILE_WRITE_EA | FILE_READ_EA | + FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | + FILE_LIST_DIRECTORY; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_AUTHENTICATED_USER_RID); + + //ace6 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x14); + ace->mask = GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_AUTHENTICATED_USER_RID); + + //ace7 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; + ace->size = const_cpu_to_le16(0x18); + ace->mask = SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES | + FILE_TRAVERSE | FILE_READ_EA | FILE_LIST_DIRECTORY; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + sub_authorities = ace->sid.sub_authority; + *sub_authorities++ = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + + //ace8 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x18); + ace->mask = GENERIC_READ | GENERIC_EXECUTE; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + sub_authorities = ace->sid.sub_authority; + *sub_authorities++ = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + *sub_authorities = const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); +} + +/** + * init_secure_sds - + * + * NTFS 3.1 - System files security decriptors + * =========================================== + * Create the security descriptor entries in $SDS data stream like they + * are in a partition, newly formatted with windows 2003 + */ +void init_secure_sds(char *sd_val) +{ + SECURITY_DESCRIPTOR_HEADER *sds; + SECURITY_DESCRIPTOR_RELATIVE *sd; + ACL *acl; + ACCESS_ALLOWED_ACE *ace; + SID *sid; + +/* + * security descriptor #1 + */ + //header + sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val); + sds->hash = const_cpu_to_le32(0xF80312F0); + sds->security_id = const_cpu_to_le32(0x0100); + sds->offset = const_cpu_to_le64(0x00); + sds->length = const_cpu_to_le32(0x7C); + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + sd->revision = 0x01; + sd->alignment = 0x00; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x58); + sd->sacl = const_cpu_to_le32(0x00); + sd->dacl = const_cpu_to_le32(0x14); + + //acl + acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = 0x02; + acl->alignment1 = 0x00; + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(0x02); + acl->alignment2 = 0x00; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x14); + ace->mask = const_cpu_to_le32(0x120089); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x18); + ace->mask = const_cpu_to_le32(0x120089); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); +/* + * security descriptor #2 + */ + //header + sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val + 0x80); + sds->hash = const_cpu_to_le32(0xB32451); + sds->security_id = const_cpu_to_le32(0x0101); + sds->offset = const_cpu_to_le64(0x80); + sds->length = const_cpu_to_le32(0x7C); + + //security descriptor relative + sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + + sizeof(SECURITY_DESCRIPTOR_HEADER)); + sd->revision = 0x01; + sd->alignment = 0x00; + sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x58); + sd->sacl = const_cpu_to_le32(0x00); + sd->dacl = const_cpu_to_le32(0x14); + + //acl + acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + acl->revision = 0x02; + acl->alignment1 = 0x00; + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(0x02); + acl->alignment2 = 0x00; + + //ace1 + ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x14); + ace->mask = const_cpu_to_le32(0x12019F); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + //ace2 + ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); + ace->type = 0x00; + ace->flags = 0x00; + ace->size = const_cpu_to_le16(0x18); + ace->mask = const_cpu_to_le32(0x12019F); + ace->sid.revision = 0x01; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + //group sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); + sid->revision = 0x01; + sid->sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + sid->sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + + return; +} diff --git a/ntfsprogs/sd.h b/ntfsprogs/sd.h new file mode 100755 index 0000000000000000000000000000000000000000..7ad3e6a79c5317de6626be144851135f48ffd16f --- /dev/null +++ b/ntfsprogs/sd.h @@ -0,0 +1,11 @@ +#ifndef _NTFS_SD_H_ +#define _NTFS_SD_H_ + +#include "types.h" + +void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len); +void init_root_sd(u8 **sd_val, int *sd_val_len); +void init_secure_sds(char *sd_val); + +#endif /* _NTFS_SD_H_ */ + diff --git a/ntfsprogs/utils.c b/ntfsprogs/utils.c new file mode 100755 index 0000000000000000000000000000000000000000..7ac31163dc4cea9565459fb41e1e76b43b533d52 --- /dev/null +++ b/ntfsprogs/utils.c @@ -0,0 +1,1204 @@ +/** + * utils.c - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2003-2006 Anton Altaparmakov + * Copyright (c) 2003 Lode Leroy + * Copyright (c) 2005-2007 Yura Pakhuchiy + * Copyright (c) 2014 Jean-Pierre Andre + * + * A set of shared functions for ntfs utilities + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#ifdef HAVE_LIBINTL_H +#include <libintl.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif + +#include "utils.h" +#include "types.h" +#include "volume.h" +#include "debug.h" +#include "dir.h" +/* #include "version.h" */ +#include "logging.h" +#include "misc.h" + +const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n"; +const char *ntfs_gpl = "This program is free software, released under the GNU " + "General Public License\nand you are welcome to redistribute it under " + "certain conditions. It comes with\nABSOLUTELY NO WARRANTY; for " + "details read the GNU General Public License to be\nfound in the file " + "\"COPYING\" distributed with this program, or online at:\n" + "http://www.gnu.org/copyleft/gpl.html\n"; + +static const char *invalid_ntfs_msg = +"The device '%s' doesn't have a valid NTFS.\n" +"Maybe you selected the wrong device? Or the whole disk instead of a\n" +"partition (e.g. /dev/hda, not /dev/hda1)? Or the other way around?\n"; + +static const char *corrupt_volume_msg = +"NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n" +"The usage of the /f parameter is very IMPORTANT! No modification was\n" +"made to NTFS by this software.\n"; + +static const char *hibernated_volume_msg = +"The NTFS partition is hibernated. Please resume Windows and turned it \n" +"off properly, so mounting could be done safely.\n"; + +static const char *unclean_journal_msg = +"Access is denied because the NTFS journal file is unclean. Choices are:\n" +" A) Shutdown Windows properly.\n" +" B) Click the 'Safely Remove Hardware' icon in the Windows taskbar\n" +" notification area before disconnecting the device.\n" +" C) Use 'Eject' from Windows Explorer to safely remove the device.\n" +" D) If you ran chkdsk previously then boot Windows again which will\n" +" automatically initialize the journal.\n" +" E) Submit 'force' option (WARNING: This solution it not recommended).\n" +" F) ntfsmount: Mount the volume read-only by using the 'ro' mount option.\n"; + +static const char *opened_volume_msg = +"Access is denied because the NTFS volume is already exclusively opened.\n" +"The volume may be already mounted, or another software may use it which\n" +"could be identified for example by the help of the 'fuser' command.\n"; + +static const char *dirty_volume_msg = +"Volume is scheduled for check.\n" +"Please boot into Windows TWICE, or use the 'force' option.\n" +"NOTE: If you had not scheduled check and last time accessed this volume\n" +"using ntfsmount and shutdown system properly, then init scripts in your\n" +"distribution are broken. Please report to your distribution developers\n" +"(NOT to us!) that init scripts kill ntfsmount or mount.ntfs-fuse during\n" +"shutdown instead of proper umount.\n"; + +static const char *fakeraid_msg = +"You seem to have a SoftRAID/FakeRAID hardware and must use an activated,\n" +"different device under /dev/mapper, (e.g. /dev/mapper/nvidia_eahaabcc1)\n" +"to mount NTFS. Please see the 'dmraid' documentation for help.\n"; + +/** + * utils_set_locale + */ +int utils_set_locale(void) +{ + const char *locale; + + locale = setlocale(LC_ALL, ""); + if (!locale) { + locale = setlocale(LC_ALL, NULL); + ntfs_log_error("Failed to set locale, using default '%s'.\n", + locale); + return 1; + } else { + return 0; + } +} + +/** + * linux-ntfs's ntfs_mbstoucs has different semantics, so we emulate it with + * ntfs-3g's. + */ +int ntfs_mbstoucs_libntfscompat(const char *ins, + ntfschar **outs, int outs_len) +{ + if(!outs) { + errno = EINVAL; + return -1; + } + else if(*outs != NULL) { + /* Note: libntfs's mbstoucs implementation allows the caller to + * specify a preallocated buffer while libntfs-3g's always + * allocates the output buffer. + */ + ntfschar *tmpstr = NULL; + int tmpstr_len; + + tmpstr_len = ntfs_mbstoucs(ins, &tmpstr); + if(tmpstr_len >= 0) { + if((tmpstr_len + 1) > outs_len) { + /* Doing a realloc instead of reusing tmpstr + * because it emulates libntfs's mbstoucs more + * closely. */ + ntfschar *re_outs = realloc(*outs, + sizeof(ntfschar)*(tmpstr_len + 1)); + if(!re_outs) + tmpstr_len = -1; + else + *outs = re_outs; + } + + if(tmpstr_len >= 0) { + /* The extra character is the \0 terminator. */ + memcpy(*outs, tmpstr, + sizeof(ntfschar)*(tmpstr_len + 1)); + } + + free(tmpstr); + } + + return tmpstr_len; + } + else + return ntfs_mbstoucs(ins, outs); +} + +/** + * utils_valid_device - Perform some safety checks on the device, before start + * @name: Full pathname of the device/file to work with + * @force: Continue regardless of problems + * + * Check that the name refers to a device and that is isn't already mounted. + * These checks can be overridden by using the force option. + * + * Return: 1 Success, we can continue + * 0 Error, we cannot use this device + */ +int utils_valid_device(const char *name, int force) +{ + unsigned long mnt_flags = 0; + struct stat st; + +#if defined(HAVE_WINDOWS_H) | defined(__CYGWIN32__) + /* FIXME: This doesn't work for Cygwin, so just return success. */ + return 1; +#endif + if (!name) { + errno = EINVAL; + return 0; + } + + if (stat(name, &st) == -1) { + if (errno == ENOENT) + ntfs_log_error("The device %s doesn't exist\n", name); + else + ntfs_log_perror("Error getting information about %s", + name); + return 0; + } + + /* Make sure the file system is not mounted. */ + if (ntfs_check_if_mounted(name, &mnt_flags)) { + ntfs_log_perror("Failed to determine whether %s is mounted", + name); + if (!force) { + ntfs_log_error("Use the force option to ignore this " + "error.\n"); + return 0; + } + ntfs_log_warning("Forced to continue.\n"); + } else if (mnt_flags & NTFS_MF_MOUNTED) { + if (!force) { + ntfs_log_error("%s", opened_volume_msg); + ntfs_log_error("You can use force option to avoid this " + "check, but this is not recommended\n" + "and may lead to data corruption.\n"); + return 0; + } + ntfs_log_warning("Forced to continue.\n"); + } + + return 1; +} + +/** + * utils_mount_volume - Mount an NTFS volume + */ +ntfs_volume * utils_mount_volume(const char *device, unsigned long flags) +{ + ntfs_volume *vol; + + if (!device) { + errno = EINVAL; + return NULL; + } + + /* Porting notes: + * + * libntfs-3g does not have the 'force' flag in ntfs_mount_flags. + * The 'force' flag in libntfs bypasses two safety checks when mounting + * read/write: + * 1. Do not mount when the VOLUME_IS_DIRTY flag in + * VOLUME_INFORMATION is set. + * 2. Do not mount when the logfile is unclean. + * + * libntfs-3g only has safety check number 2. The dirty flag is simply + * ignored because we are confident that we can handle a dirty volume. + * So we treat NTFS_MNT_RECOVER like NTFS_MNT_FORCE, knowing that the + * first check is always bypassed. + */ + + if (!utils_valid_device(device, flags & NTFS_MNT_RECOVER)) + return NULL; + + vol = ntfs_mount(device, flags); + if (!vol) { + ntfs_log_perror("Failed to mount '%s'", device); + if (errno == EINVAL) + ntfs_log_error(invalid_ntfs_msg, device); + else if (errno == EIO) + ntfs_log_error("%s", corrupt_volume_msg); + else if (errno == EPERM) + ntfs_log_error("%s", hibernated_volume_msg); + else if (errno == EOPNOTSUPP) + ntfs_log_error("%s", unclean_journal_msg); + else if (errno == EBUSY) + ntfs_log_error("%s", opened_volume_msg); + else if (errno == ENXIO) + ntfs_log_error("%s", fakeraid_msg); + return NULL; + } + + /* Porting notes: + * libntfs-3g does not record whether the volume log file was dirty + * before mount, so we can only warn if the VOLUME_IS_DIRTY flag is set + * in VOLUME_INFORMATION. */ + if (vol->flags & VOLUME_IS_DIRTY) { + if (!(flags & NTFS_MNT_RECOVER)) { + ntfs_log_error("%s", dirty_volume_msg); + ntfs_umount(vol, FALSE); + return NULL; + } + ntfs_log_error("WARNING: Dirty volume mount was forced by the " + "'force' mount option.\n"); + } + return vol; +} + +/** + * utils_parse_size - Convert a string representing a size + * @value: String to be parsed + * @size: Parsed size + * @scale: Whether or not to allow a suffix to scale the value + * + * Read a string and convert it to a number. Strings may be suffixed to scale + * them. Any number without a suffix is assumed to be in bytes. + * + * Suffix Description Multiple + * [tT] Terabytes 10^12 + * [gG] Gigabytes 10^9 + * [mM] Megabytes 10^6 + * [kK] Kilobytes 10^3 + * + * Notes: + * Only the first character of the suffix is read. + * The multipliers are decimal thousands, not binary: 1000, not 1024. + * If parse_size fails, @size will not be changed + * + * Return: 1 Success + * 0 Error, the string was malformed + */ +int utils_parse_size(const char *value, s64 *size, BOOL scale) +{ + long long result; + char *suffix = NULL; + + if (!value || !size) { + errno = EINVAL; + return 0; + } + + ntfs_log_debug("Parsing size '%s'.\n", value); + + result = strtoll(value, &suffix, 0); + if (result < 0 || errno == ERANGE) { + ntfs_log_error("Invalid size '%s'.\n", value); + return 0; + } + + if (!suffix) { + ntfs_log_error("Internal error, strtoll didn't return a suffix.\n"); + return 0; + } + + if (scale) { + switch (suffix[0]) { + case 't': case 'T': result *= 1000; + case 'g': case 'G': result *= 1000; + case 'm': case 'M': result *= 1000; + case 'k': case 'K': result *= 1000; + case '-': case 0: + break; + default: + ntfs_log_error("Invalid size suffix '%s'. Use T, G, M, or K.\n", suffix); + return 0; + } + } else { + if ((suffix[0] != '-') && (suffix[0] != 0)) { + ntfs_log_error("Invalid number '%.*s'.\n", (int)(suffix - value + 1), value); + return 0; + } + } + + ntfs_log_debug("Parsed size = %lld.\n", result); + *size = result; + return 1; +} + +/** + * utils_parse_range - Convert a string representing a range of numbers + * @string: The string to be parsed + * @start: The beginning of the range will be stored here + * @finish: The end of the range will be stored here + * + * Read a string of the form n-m. If the lower end is missing, zero will be + * substituted. If the upper end is missing LONG_MAX will be used. If the + * string cannot be parsed correctly, @start and @finish will not be changed. + * + * Return: 1 Success, a valid string was found + * 0 Error, the string was not a valid range + */ +int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale) +{ + s64 a, b; + char *middle; + + if (!string || !start || !finish) { + errno = EINVAL; + return 0; + } + + middle = strchr(string, '-'); + if (string == middle) { + ntfs_log_debug("Range has no beginning, defaulting to 0.\n"); + a = 0; + } else { + if (!utils_parse_size(string, &a, scale)) + return 0; + } + + if (middle) { + if (middle[1] == 0) { + b = LONG_MAX; // XXX ULLONG_MAX + ntfs_log_debug("Range has no end, defaulting to %lld.\n", b); + } else { + if (!utils_parse_size(middle+1, &b, scale)) + return 0; + } + } else { + b = a; + } + + ntfs_log_debug("Range '%s' = %lld - %lld\n", string, a, b); + + *start = a; + *finish = b; + return 1; +} + +/** + * find_attribute - Find an attribute of the given type + * @type: An attribute type, e.g. AT_FILE_NAME + * @ctx: A search context, created using ntfs_get_attr_search_ctx + * + * Using the search context to keep track, find the first/next occurrence of a + * given attribute type. + * + * N.B. This will return a pointer into @mft. As long as the search context + * has been created without an inode, it won't overflow the buffer. + * + * Return: Pointer Success, an attribute was found + * NULL Error, no matching attributes were found + */ +ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) +{ + if (!ctx) { + errno = EINVAL; + return NULL; + } + + if (ntfs_attr_lookup(type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) { + ntfs_log_debug("find_attribute didn't find an attribute of type: 0x%02x.\n", type); + return NULL; /* None / no more of that type */ + } + + ntfs_log_debug("find_attribute found an attribute of type: 0x%02x.\n", type); + return ctx->attr; +} + +/** + * find_first_attribute - Find the first attribute of a given type + * @type: An attribute type, e.g. AT_FILE_NAME + * @mft: A buffer containing a raw MFT record + * + * Search through a raw MFT record for an attribute of a given type. + * The return value is a pointer into the MFT record that was supplied. + * + * N.B. This will return a pointer into @mft. The pointer won't stray outside + * the buffer, since we created the search context without an inode. + * + * Return: Pointer Success, an attribute was found + * NULL Error, no matching attributes were found + */ +ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft) +{ + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *rec; + + if (!mft) { + errno = EINVAL; + return NULL; + } + + ctx = ntfs_attr_get_search_ctx(NULL, mft); + if (!ctx) { + ntfs_log_error("Couldn't create a search context.\n"); + return NULL; + } + + rec = find_attribute(type, ctx); + ntfs_attr_put_search_ctx(ctx); + if (rec) + ntfs_log_debug("find_first_attribute: found attr of type 0x%02x.\n", type); + else + ntfs_log_debug("find_first_attribute: didn't find attr of type 0x%02x.\n", type); + return rec; +} + +/** + * utils_inode_get_name + * + * using inode + * get filename + * add name to list + * get parent + * if parent is 5 (/) stop + * get inode of parent + */ +#define max_path 20 +int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) +{ + // XXX option: names = posix/win32 or dos + // flags: path, filename, or both + + + ntfs_volume *vol; + ntfs_attr_search_ctx *ctx; + ATTR_RECORD *rec; + FILE_NAME_ATTR *attr; + int name_space; + MFT_REF parent = FILE_root; + char *names[max_path + 1];// XXX ntfs_malloc? and make max bigger? + int i, len, offset = 0; + + if (!inode || !buffer) { + errno = EINVAL; + return 0; + } + + vol = inode->vol; + + //ntfs_log_debug("sizeof(char*) = %d, sizeof(names) = %d\n", sizeof(char*), sizeof(names)); + memset(names, 0, sizeof(names)); + + for (i = 0; i < max_path; i++) { + + ctx = ntfs_attr_get_search_ctx(inode, NULL); + if (!ctx) { + ntfs_log_error("Couldn't create a search context.\n"); + return 0; + } + + //ntfs_log_debug("i = %d, inode = %p (%lld)\n", i, inode, inode->mft_no); + + name_space = 4; + while ((rec = find_attribute(AT_FILE_NAME, ctx))) { + /* We know this will always be resident. */ + attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset)); + + if (attr->file_name_type > name_space) { //XXX find the ... + continue; + } + + name_space = attr->file_name_type; + parent = le64_to_cpu(attr->parent_directory); + + if (names[i]) { + free(names[i]); + names[i] = NULL; + } + + if (ntfs_ucstombs(attr->file_name, attr->file_name_length, + &names[i], 0) < 0) { + char *temp; + ntfs_log_error("Couldn't translate filename to current locale.\n"); + temp = ntfs_malloc(30); + if (!temp) + return 0; + snprintf(temp, 30, "<MFT%llu>", (unsigned + long long)inode->mft_no); + names[i] = temp; + } + + //ntfs_log_debug("names[%d] %s\n", i, names[i]); + //ntfs_log_debug("parent = %lld\n", MREF(parent)); + } + + ntfs_attr_put_search_ctx(ctx); + + if (i > 0) /* Don't close the original inode */ + ntfs_inode_close(inode); + + if (MREF(parent) == FILE_root) { /* The root directory, stop. */ + //ntfs_log_debug("inode 5\n"); + break; + } + + inode = ntfs_inode_open(vol, parent); + if (!inode) { + ntfs_log_error("Couldn't open inode %llu.\n", + (unsigned long long)MREF(parent)); + break; + } + } + + if (i >= max_path) { + /* If we get into an infinite loop, we'll end up here. */ + ntfs_log_error("The directory structure is too deep (over %d) nested directories.\n", max_path); + return 0; + } + + /* Assemble the names in the correct order. */ + for (i = max_path; i >= 0; i--) { + if (!names[i]) + continue; + + len = snprintf(buffer + offset, bufsize - offset, "%c%s", PATH_SEP, names[i]); + if (len >= (bufsize - offset)) { + ntfs_log_error("Pathname was truncated.\n"); + break; + } + + offset += len; + } + + /* Free all the allocated memory */ + for (i = 0; i < max_path; i++) + free(names[i]); + + ntfs_log_debug("Pathname: %s\n", buffer); + + return 1; +} +#undef max_path + +/** + * utils_attr_get_name + */ +int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize) +{ + int len, namelen; + char *name; + ATTR_DEF *attrdef; + + // flags: attr, name, or both + if (!attr || !buffer) { + errno = EINVAL; + return 0; + } + + attrdef = ntfs_attr_find_in_attrdef(vol, attr->type); + if (attrdef) { + name = NULL; + namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name)); + if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) { + ntfs_log_error("Couldn't translate attribute type to " + "current locale.\n"); + // <UNKNOWN>? + return 0; + } + len = snprintf(buffer, bufsize, "%s", name); + } else { + ntfs_log_error("Unknown attribute type 0x%02x\n", attr->type); + len = snprintf(buffer, bufsize, "<UNKNOWN>"); + } + + if (len >= bufsize) { + ntfs_log_error("Attribute type was truncated.\n"); + return 0; + } + + if (!attr->name_length) { + return 0; + } + + buffer += len; + bufsize -= len; + + name = NULL; + namelen = attr->name_length; + if (ntfs_ucstombs((ntfschar *)((char *)attr + + le16_to_cpu(attr->name_offset)), + namelen, &name, 0) < 0) { + ntfs_log_error("Couldn't translate attribute name to current " + "locale.\n"); + // <UNKNOWN>? + len = snprintf(buffer, bufsize, "<UNKNOWN>"); + return 0; + } + + len = snprintf(buffer, bufsize, "(%s)", name); + free(name); + + if (len >= bufsize) { + ntfs_log_error("Attribute name was truncated.\n"); + return 0; + } + + return 0; +} + +/** + * utils_cluster_in_use - Determine if a cluster is in use + * @vol: An ntfs volume obtained from ntfs_mount + * @lcn: The Logical Cluster Number to test + * + * The metadata file $Bitmap has one binary bit representing each cluster on + * disk. The bit will be set for each cluster that is in use. The function + * reads the relevant part of $Bitmap into a buffer and tests the bit. + * + * This function has a static buffer in which it caches a section of $Bitmap. + * If the lcn, being tested, lies outside the range, the buffer will be + * refreshed. @bmplcn stores offset to the first bit (in bits) stored in the + * buffer. + * + * NOTE: Be very carefull with shifts by 3 everywhere in this function. + * + * Return: 1 Cluster is in use + * 0 Cluster is free space + * -1 Error occurred + */ +int utils_cluster_in_use(ntfs_volume *vol, long long lcn) +{ + static unsigned char buffer[512]; + static long long bmplcn = -(sizeof(buffer) << 3); + int byte, bit; + ntfs_attr *attr; + + if (!vol) { + errno = EINVAL; + return -1; + } + + /* Does lcn lie in the section of $Bitmap we already have cached? */ + if ((lcn < bmplcn) + || (lcn >= (long long)(bmplcn + (sizeof(buffer) << 3)))) { + ntfs_log_debug("Bit lies outside cache.\n"); + attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); + if (!attr) { + ntfs_log_perror("Couldn't open $Bitmap"); + return -1; + } + + /* Mark the buffer as in use, in case the read is shorter. */ + memset(buffer, 0xFF, sizeof(buffer)); + bmplcn = lcn & (~((sizeof(buffer) << 3) - 1)); + + if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer), + buffer) < 0) { + ntfs_log_perror("Couldn't read $Bitmap"); + ntfs_attr_close(attr); + return -1; + } + + ntfs_log_debug("Reloaded bitmap buffer.\n"); + ntfs_attr_close(attr); + } + + bit = 1 << (lcn & 7); + byte = (lcn >> 3) & (sizeof(buffer) - 1); + ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, " + "in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] & + bit); + + return (buffer[byte] & bit); +} + +/** + * utils_mftrec_in_use - Determine if a MFT Record is in use + * @vol: An ntfs volume obtained from ntfs_mount + * @mref: MFT Reference (inode number) + * + * The metadata file $BITMAP has one binary bit representing each record in the + * MFT. The bit will be set for each record that is in use. The function + * reads the relevant part of $BITMAP into a buffer and tests the bit. + * + * This function has a static buffer in which it caches a section of $BITMAP. + * If the mref, being tested, lies outside the range, the buffer will be + * refreshed. + * + * Return: 1 MFT Record is in use + * 0 MFT Record is unused + * -1 Error occurred + */ +int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref) +{ + static u8 buffer[512]; + static s64 bmpmref = -(sizeof(buffer) << 3) - 1; /* Which bit of $BITMAP is in the buffer */ + int byte, bit; + + ntfs_log_trace("Entering.\n"); + + if (!vol) { + errno = EINVAL; + return -1; + } + + /* Does mref lie in the section of $Bitmap we already have cached? */ + if (((s64)MREF(mref) < bmpmref) + || ((s64)MREF(mref) >= (s64)(bmpmref + (sizeof(buffer) << 3)))) { + ntfs_log_debug("Bit lies outside cache.\n"); + + /* Mark the buffer as not in use, in case the read is shorter. */ + memset(buffer, 0, sizeof(buffer)); + bmpmref = mref & (~((sizeof(buffer) << 3) - 1)); + + if (ntfs_attr_pread(vol->mftbmp_na, (bmpmref>>3), sizeof(buffer), buffer) < 0) { + ntfs_log_perror("Couldn't read $MFT/$BITMAP"); + return -1; + } + + ntfs_log_debug("Reloaded bitmap buffer.\n"); + } + + bit = 1 << (mref & 7); + byte = (mref >> 3) & (sizeof(buffer) - 1); + ntfs_log_debug("cluster = %lld, bmpmref = %lld, byte = %d, bit = %d, in use %d\n", mref, bmpmref, byte, bit, buffer[byte] & bit); + + return (buffer[byte] & bit); +} + +/** + * __metadata + */ +static int __metadata(ntfs_volume *vol, u64 num) +{ + if (num <= FILE_UpCase) + return 1; + if (!vol) + return -1; + if ((vol->major_ver == 3) && (num == FILE_Extend)) + return 1; + + return 0; +} + +/** + * utils_is_metadata - Determine if an inode represents a metadata file + * @inode: An ntfs inode to be tested + * + * A handful of files in the volume contain filesystem data - metadata. + * They can be identified by their inode number (offset in MFT/$DATA) or by + * their parent. + * + * Return: 1 inode is a metadata file + * 0 inode is not a metadata file + * -1 Error occurred + */ +int utils_is_metadata(ntfs_inode *inode) +{ + ntfs_volume *vol; + ATTR_RECORD *rec; + FILE_NAME_ATTR *attr; + MFT_RECORD *file; + u64 num; + + if (!inode) { + errno = EINVAL; + return -1; + } + + vol = inode->vol; + if (!vol) + return -1; + + num = inode->mft_no; + if (__metadata(vol, num) == 1) + return 1; + + file = inode->mrec; + if (file && (file->base_mft_record != 0)) { + num = MREF_LE(file->base_mft_record); + if (__metadata(vol, num) == 1) + return 1; + } + + rec = find_first_attribute(AT_FILE_NAME, inode->mrec); + if (!rec) + return -1; + + /* We know this will always be resident. */ + attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->value_offset)); + + num = MREF_LE(attr->parent_directory); + if ((num != FILE_root) && (__metadata(vol, num) == 1)) + return 1; + + return 0; +} + +/** + * utils_dump_mem - Display a block of memory in hex and ascii + * @buf: Buffer to be displayed + * @start: Offset into @buf to start from + * @length: Number of bytes to display + * @flags: Options to change the style of the output + * + * Display a block of memory in a tradition hex-dump manner. + * Optionally the ascii part can be turned off. + * + * The flags, described fully in utils.h, default to 0 (DM_DEFAULTS). + * Examples are: DM_INDENT (indent the output by one tab); DM_RED (colour the + * output); DM_NO_ASCII (only print the hex values). + */ +void utils_dump_mem(void *buf, int start, int length, int flags) +{ + int off, i, s, e, col; + u8 *mem = buf; + + s = start & ~15; // round down + e = (start + length + 15) & ~15; // round up + + for (off = s; off < e; off += 16) { + col = 30; + if (flags & DM_RED) + col += 1; + if (flags & DM_GREEN) + col += 2; + if (flags & DM_BLUE) + col += 4; + if (flags & DM_INDENT) + ntfs_log_debug("\t"); + if (flags & DM_BOLD) + ntfs_log_debug("\e[01m"); + if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD)) + ntfs_log_debug("\e[%dm", col); + if (off == s) + ntfs_log_debug("%6.6x ", start); + else + ntfs_log_debug("%6.6x ", off); + + for (i = 0; i < 16; i++) { + if ((i == 8) && (!(flags & DM_NO_DIVIDER))) + ntfs_log_debug(" -"); + if (((off+i) >= start) && ((off+i) < (start+length))) + ntfs_log_debug(" %02X", mem[off+i]); + else + ntfs_log_debug(" "); + } + if (!(flags & DM_NO_ASCII)) { + ntfs_log_debug(" "); + for (i = 0; i < 16; i++) { + if (((off+i) < start) || ((off+i) >= (start+length))) + ntfs_log_debug(" "); + else if (isprint(mem[off + i])) + ntfs_log_debug("%c", mem[off + i]); + else + ntfs_log_debug("."); + } + } + if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD)) + ntfs_log_debug("\e[0m"); + ntfs_log_debug("\n"); + } +} + + +/** + * mft_get_search_ctx + */ +struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol) +{ + struct mft_search_ctx *ctx; + + if (!vol) { + errno = EINVAL; + return NULL; + } + + ctx = (struct mft_search_ctx*)calloc(1, sizeof *ctx); + + ctx->mft_num = -1; + ctx->vol = vol; + + return ctx; +} + +/** + * mft_put_search_ctx + */ +void mft_put_search_ctx(struct mft_search_ctx *ctx) +{ + if (!ctx) + return; + if (ctx->inode) + ntfs_inode_close(ctx->inode); + free(ctx); +} + +/** + * mft_next_record + */ +int mft_next_record(struct mft_search_ctx *ctx) +{ + s64 nr_mft_records; + ATTR_RECORD *attr10 = NULL; + ATTR_RECORD *attr20 = NULL; + ATTR_RECORD *attr80 = NULL; + ntfs_attr_search_ctx *attr_ctx; + + if (!ctx) { + errno = EINVAL; + return -1; + } + + if (ctx->inode) { + ntfs_inode_close(ctx->inode); + ctx->inode = NULL; + } + + nr_mft_records = ctx->vol->mft_na->initialized_size >> + ctx->vol->mft_record_size_bits; + + for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) { + int in_use; + + ctx->flags_match = 0; + in_use = utils_mftrec_in_use(ctx->vol, (MFT_REF) ctx->mft_num); + if (in_use == -1) { + ntfs_log_error("Error reading inode %llu. Aborting.\n", + (unsigned long long)ctx->mft_num); + return -1; + } + + if (in_use) { + ctx->flags_match |= FEMR_IN_USE; + + ctx->inode = ntfs_inode_open(ctx->vol, (MFT_REF) ctx->mft_num); + if (ctx->inode == NULL) { + MFT_RECORD *mrec; + int r; + MFT_REF base_inode; + + mrec = (MFT_RECORD*)NULL; + r = ntfs_file_record_read(ctx->vol, + (MFT_REF) ctx->mft_num, &mrec, NULL); + if (r || !mrec || !mrec->base_mft_record) + ntfs_log_error( + "Error reading inode %lld.\n", + (long long)ctx->mft_num); + else { + base_inode = le64_to_cpu( + mrec->base_mft_record); + ntfs_log_error("Inode %lld is an " + "extent of inode %lld.\n", + (long long)ctx->mft_num, + (long long)MREF(base_inode)); + } + free (mrec); + continue; + } + + attr10 = find_first_attribute(AT_STANDARD_INFORMATION, ctx->inode->mrec); + attr20 = find_first_attribute(AT_ATTRIBUTE_LIST, ctx->inode->mrec); + attr80 = find_first_attribute(AT_DATA, ctx->inode->mrec); + + if (attr10) + ctx->flags_match |= FEMR_BASE_RECORD; + else + ctx->flags_match |= FEMR_NOT_BASE_RECORD; + + if (attr20) + ctx->flags_match |= FEMR_BASE_RECORD; + + if (attr80) + ctx->flags_match |= FEMR_FILE; + + if (ctx->flags_search & FEMR_DIR) { + attr_ctx = ntfs_attr_get_search_ctx(ctx->inode, NULL); + if (attr_ctx) { + if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, 0, 0, NULL, 0, attr_ctx) == 0) + ctx->flags_match |= FEMR_DIR; + + ntfs_attr_put_search_ctx(attr_ctx); + } else { + ntfs_log_error("Couldn't create a search context.\n"); + return -1; + } + } + + switch (utils_is_metadata(ctx->inode)) { + case 1: ctx->flags_match |= FEMR_METADATA; break; + case 0: ctx->flags_match |= FEMR_NOT_METADATA; break; + default: + ctx->flags_match |= FEMR_NOT_METADATA; break; + //ntfs_log_error("Error reading inode %lld.\n", ctx->mft_num); + //return -1; + } + + } else { // !in_use + ntfs_attr *mft; + + ctx->flags_match |= FEMR_NOT_IN_USE; + + ctx->inode = (ntfs_inode*)calloc(1, sizeof(*ctx->inode)); + if (!ctx->inode) { + ntfs_log_error("Out of memory. Aborting.\n"); + return -1; + } + + ctx->inode->mft_no = ctx->mft_num; + ctx->inode->vol = ctx->vol; + ctx->inode->mrec = ntfs_malloc(ctx->vol->mft_record_size); + if (!ctx->inode->mrec) { + free(ctx->inode); // == ntfs_inode_close + return -1; + } + + mft = ntfs_attr_open(ctx->vol->mft_ni, AT_DATA, + AT_UNNAMED, 0); + if (!mft) { + ntfs_log_perror("Couldn't open $MFT/$DATA"); + // free / close + return -1; + } + + if (ntfs_attr_pread(mft, ctx->vol->mft_record_size * ctx->mft_num, ctx->vol->mft_record_size, ctx->inode->mrec) < ctx->vol->mft_record_size) { + ntfs_log_perror("Couldn't read MFT Record %llu", + (unsigned long long) ctx->mft_num); + // free / close + ntfs_attr_close(mft); + return -1; + } + + ntfs_attr_close(mft); + } + + if (ctx->flags_match & ctx->flags_search) { + break; + } + + if (ntfs_inode_close(ctx->inode)) { + ntfs_log_error("Error closing inode %llu.\n", + (unsigned long long)ctx->mft_num); + return -errno; + } + + ctx->inode = NULL; + } + + return (ctx->inode == NULL); +} + +#ifdef HAVE_WINDOWS_H + +/* + * Translate formats for older Windows + * + * Up to Windows XP, msvcrt.dll does not support long long format + * specifications (%lld, %llx, etc). We have to translate them + * to %I64. + */ + +char *ntfs_utils_reformat(char *out, int sz, const char *fmt) +{ + const char *f; + char *p; + int i; + enum { F_INIT, F_PERCENT, F_FIRST } state; + + i = 0; + f = fmt; + p = out; + state = F_INIT; + while (*f && ((i + 3) < sz)) { + switch (state) { + case F_INIT : + if (*f == '%') + state = F_PERCENT; + *p++ = *f++; + i++; + break; + case F_PERCENT : + if (*f == 'l') { + state = F_FIRST; + f++; + } else { + if (((*f < '0') || (*f > '9')) + && (*f != '*') && (*f != '-')) + state = F_INIT; + *p++ = *f++; + i++; + } + break; + case F_FIRST : + if (*f == 'l') { + *p++ = 'I'; + *p++ = '6'; + *p++ = '4'; + f++; + i += 3; + } else { + *p++ = 'l'; + *p++ = *f++; + i += 2; + } + state = F_INIT; + break; + } + } + *p++ = 0; + return (out); +} + +#endif diff --git a/ntfsprogs/utils.h b/ntfsprogs/utils.h new file mode 100755 index 0000000000000000000000000000000000000000..8b6bfae5e8ba4a486e6004f67a8fc00797f435df --- /dev/null +++ b/ntfsprogs/utils.h @@ -0,0 +1,137 @@ +/* + * utils.h - Part of the Linux-NTFS project. + * + * Copyright (c) 2002-2005 Richard Russon + * Copyright (c) 2004 Anton Altaparmakov + * + * A set of shared functions for ntfs utilities + * + * 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 (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_UTILS_H_ +#define _NTFS_UTILS_H_ + +#include "config.h" + +#include "types.h" +#include "layout.h" +#include "volume.h" + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif + +extern const char *ntfs_bugs; +extern const char *ntfs_gpl; + +int utils_set_locale(void); +int utils_parse_size(const char *value, s64 *size, BOOL scale); +int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale); +int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize); +int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize); +int utils_cluster_in_use(ntfs_volume *vol, long long lcn); +int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref); +int utils_is_metadata(ntfs_inode *inode); +void utils_dump_mem(void *buf, int start, int length, int flags); + +ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx); +ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft); + +int utils_valid_device(const char *name, int force); +ntfs_volume * utils_mount_volume(const char *device, unsigned long flags); + +/** + * defines... + * if *not in use* then the other flags are ignored? + */ +#define FEMR_IN_USE (1 << 0) +#define FEMR_NOT_IN_USE (1 << 1) +#define FEMR_FILE (1 << 2) // $DATA +#define FEMR_DIR (1 << 3) // $INDEX_ROOT, "$I30" +#define FEMR_METADATA (1 << 4) +#define FEMR_NOT_METADATA (1 << 5) +#define FEMR_BASE_RECORD (1 << 6) +#define FEMR_NOT_BASE_RECORD (1 << 7) +#define FEMR_ALL_RECORDS 0xFF + +/** + * struct mft_search_ctx + */ +struct mft_search_ctx { + int flags_search; + int flags_match; + ntfs_inode *inode; + ntfs_volume *vol; + u64 mft_num; +}; + +struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol); +void mft_put_search_ctx(struct mft_search_ctx *ctx); +int mft_next_record(struct mft_search_ctx *ctx); + +// Flags for dump mem +#define DM_DEFAULTS 0 +#define DM_NO_ASCII (1 << 0) +#define DM_NO_DIVIDER (1 << 1) +#define DM_INDENT (1 << 2) +#define DM_RED (1 << 3) +#define DM_GREEN (1 << 4) +#define DM_BLUE (1 << 5) +#define DM_BOLD (1 << 6) + +/* MAX_PATH definition was missing in ntfs-3g's headers. */ +#ifndef MAX_PATH +#define MAX_PATH 1024 +#endif + +#ifdef HAVE_WINDOWS_H +/* + * Macroes to hide the needs to translate formats on older Windows + */ +#define MAX_FMT 1536 +char *ntfs_utils_reformat(char *out, int sz, const char *fmt); +#define ntfs_log_redirect(fn,fi,li,le,d,fmt, args...) \ + do { char buf[MAX_FMT]; ntfs_log_redirect(fn,fi,li,le,d, \ + ntfs_utils_reformat(buf,MAX_FMT,fmt), args); } while (0) +#define printf(fmt, args...) \ + do { char buf[MAX_FMT]; \ + printf(ntfs_utils_reformat(buf,MAX_FMT,fmt), args); } while (0) +#define fprintf(str, fmt, args...) \ + do { char buf[MAX_FMT]; \ + fprintf(str, ntfs_utils_reformat(buf,MAX_FMT,fmt), args); } while (0) +#define vfprintf(file, fmt, args) \ + do { char buf[MAX_FMT]; vfprintf(file, \ + ntfs_utils_reformat(buf,MAX_FMT,fmt), args); } while (0) +#endif + +/** + * linux-ntfs's ntfs_mbstoucs has different semantics, so we emulate it with + * ntfs-3g's. + */ +int ntfs_mbstoucs_libntfscompat(const char *ins, + ntfschar **outs, int outs_len); + +/* This simple utility function was missing from libntfs-3g. */ +static __inline__ ntfschar *ntfs_attr_get_name(ATTR_RECORD *attr) +{ + return (ntfschar*)((u8*)attr + le16_to_cpu(attr->name_offset)); +} + +#endif /* _NTFS_UTILS_H_ */ diff --git a/src/Android.mk b/src/Android.mk new file mode 100755 index 0000000000000000000000000000000000000000..cddce78b3cd7365798610d9df22c1bc7fa9950d9 --- /dev/null +++ b/src/Android.mk @@ -0,0 +1,76 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + ntfs-3g_common.c \ + ntfs-3g.c + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/../src \ + $(LOCAL_PATH)/../include/fuse-lite \ + $(LOCAL_PATH)/../include/ntfs-3g + +LOCAL_CFLAGS := -O2 -g -W -Wall \ + -D_LARGEFILE_SOURCE \ + -D_FILE_OFFSET_BITS=64 \ + -DHAVE_CONFIG_H + +LOCAL_MODULE := ntfs-3g +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := libc libutils libntfs-3g +LOCAL_STATIC_LIBRARIES := libfuse-lite + +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + ntfs-3g_common.c \ + lowntfs-3g.c + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/../src \ + $(LOCAL_PATH)/../include/fuse-lite \ + $(LOCAL_PATH)/../include/ntfs-3g + +LOCAL_CFLAGS := -O2 -g -W -Wall \ + -D_LARGEFILE_SOURCE \ + -D_FILE_OFFSET_BITS=64 \ + -DHAVE_CONFIG_H + +LOCAL_MODULE := lowntfs-3g +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := libc libutils libntfs-3g +LOCAL_STATIC_LIBRARIES := libfuse-lite + +#include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + usermap.c + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/../src \ + $(LOCAL_PATH)/../include/fuse-lite \ + $(LOCAL_PATH)/../include/ntfs-3g + +LOCAL_CFLAGS := -O2 -g -W -Wall \ + -D_LARGEFILE_SOURCE \ + -D_FILE_OFFSET_BITS=64 \ + -DHAVE_CONFIG_H + +LOCAL_MODULE := usermap +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := libc libutils libntfs-3g +LOCAL_STATIC_LIBRARIES := libfuse-lite + +#include $(BUILD_EXECUTABLE) + + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100755 index 0000000000000000000000000000000000000000..89ac5ce09f8b36f59b7d7869d056d473ecbab777 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,86 @@ + +EXTRA_DIST = secaudit.h ntfs-3g_common.h + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in + +if FUSE_INTERNAL +FUSE_CFLAGS = -I$(top_srcdir)/include/fuse-lite +FUSE_LIBS = $(top_builddir)/libfuse-lite/libfuse-lite.la +else +FUSE_CFLAGS = $(FUSE_MODULE_CFLAGS) +FUSE_LIBS = $(FUSE_MODULE_LIBS) +endif + +if ENABLE_NTFS_3G + +bin_PROGRAMS = ntfs-3g.probe \ + ntfs-3g.usermap \ + ntfs-3g.secaudit +rootbin_PROGRAMS = ntfs-3g lowntfs-3g +rootsbin_DATA = #Create directory +man_MANS = ntfs-3g.8 ntfs-3g.probe.8 \ + ntfs-3g.usermap.8 \ + ntfs-3g.secaudit.8 + +ntfs_3g_LDADD = $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la +if REALLYSTATIC +ntfs_3g_LDFLAGS = $(AM_LDFLAGS) -all-static +endif +ntfs_3g_CFLAGS = \ + $(AM_CFLAGS) \ + -DFUSE_USE_VERSION=26 \ + $(FUSE_CFLAGS) \ + -I$(top_srcdir)/include/ntfs-3g +ntfs_3g_SOURCES = ntfs-3g.c ntfs-3g_common.c + +lowntfs_3g_LDADD = $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la +if REALLYSTATIC +lowntfs_3g_LDFLAGS = $(AM_LDFLAGS) -all-static +endif +lowntfs_3g_CFLAGS = \ + $(AM_CFLAGS) \ + -DFUSE_USE_VERSION=26 \ + $(FUSE_CFLAGS) \ + -I$(top_srcdir)/include/ntfs-3g +lowntfs_3g_SOURCES = lowntfs-3g.c ntfs-3g_common.c + +ntfs_3g_probe_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la +ntfs_3g_usermap_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la +ntfs_3g_secaudit_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la +if REALLYSTATIC +ntfs_3g_probe_LDFLAGS = $(AM_LDFLAGS) -all-static +ntfs_3g_usermap_LDFLAGS = $(AM_LDFLAGS) -all-static +ntfs_3g_secaudit_LDFLAGS = $(AM_LDFLAGS) -all-static +endif +ntfs_3g_probe_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g +ntfs_3g_usermap_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g +ntfs_3g_secaudit_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g +ntfs_3g_probe_SOURCES = ntfs-3g.probe.c +ntfs_3g_usermap_SOURCES = usermap.c +ntfs_3g_secaudit_SOURCES = secaudit.c + +drivers : $(FUSE_LIBS) ntfs-3g lowntfs-3g + +if RUN_LDCONFIG +install-exec-hook: + $(LDCONFIG) +endif + +if ENABLE_MOUNT_HELPER +install-exec-local: install-rootbinPROGRAMS + $(MKDIR_P) "$(DESTDIR)/sbin" + $(LN_S) -f "$(rootbindir)/ntfs-3g" "$(DESTDIR)/sbin/mount.ntfs-3g" + $(LN_S) -f "$(rootbindir)/lowntfs-3g" "$(DESTDIR)/sbin/mount.lowntfs-3g" +endif + +install-data-local: install-man8 + $(LN_S) -f ntfs-3g.8 "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8" + $(LN_S) -f ntfs-3g.8 "$(DESTDIR)$(man8dir)/mount.lowntfs-3g.8" + +uninstall-local: + $(RM) -f "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8" +if ENABLE_MOUNT_HELPER + $(RM) -f "$(DESTDIR)/sbin/mount.ntfs-3g" "$(DESTDIR)/sbin/mount.lowntfs-3g" +endif + +endif # ENABLE_NTFS_3G diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100755 index 0000000000000000000000000000000000000000..ce94059e54966a4a470cf4cbd4603332bbdde560 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,1057 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@ENABLE_NTFS_3G_TRUE@bin_PROGRAMS = ntfs-3g.probe$(EXEEXT) \ +@ENABLE_NTFS_3G_TRUE@ ntfs-3g.usermap$(EXEEXT) \ +@ENABLE_NTFS_3G_TRUE@ ntfs-3g.secaudit$(EXEEXT) +@ENABLE_NTFS_3G_TRUE@rootbin_PROGRAMS = ntfs-3g$(EXEEXT) \ +@ENABLE_NTFS_3G_TRUE@ lowntfs-3g$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/ntfs-3g.8.in $(srcdir)/ntfs-3g.probe.8.in \ + $(srcdir)/ntfs-3g.usermap.8.in $(srcdir)/ntfs-3g.secaudit.8.in \ + $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = ntfs-3g.8 ntfs-3g.probe.8 ntfs-3g.usermap.8 \ + ntfs-3g.secaudit.8 +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(rootbindir)" \ + "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(rootsbindir)" +PROGRAMS = $(bin_PROGRAMS) $(rootbin_PROGRAMS) +am__lowntfs_3g_SOURCES_DIST = lowntfs-3g.c ntfs-3g_common.c +@ENABLE_NTFS_3G_TRUE@am_lowntfs_3g_OBJECTS = \ +@ENABLE_NTFS_3G_TRUE@ lowntfs_3g-lowntfs-3g.$(OBJEXT) \ +@ENABLE_NTFS_3G_TRUE@ lowntfs_3g-ntfs-3g_common.$(OBJEXT) +lowntfs_3g_OBJECTS = $(am_lowntfs_3g_OBJECTS) +am__DEPENDENCIES_1 = +@FUSE_INTERNAL_FALSE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +@FUSE_INTERNAL_TRUE@am__DEPENDENCIES_2 = $(top_builddir)/libfuse-lite/libfuse-lite.la +@ENABLE_NTFS_3G_TRUE@lowntfs_3g_DEPENDENCIES = $(am__DEPENDENCIES_2) \ +@ENABLE_NTFS_3G_TRUE@ $(top_builddir)/libntfs-3g/libntfs-3g.la +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +lowntfs_3g_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(lowntfs_3g_CFLAGS) \ + $(CFLAGS) $(lowntfs_3g_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfs_3g_SOURCES_DIST = ntfs-3g.c ntfs-3g_common.c +@ENABLE_NTFS_3G_TRUE@am_ntfs_3g_OBJECTS = ntfs_3g-ntfs-3g.$(OBJEXT) \ +@ENABLE_NTFS_3G_TRUE@ ntfs_3g-ntfs-3g_common.$(OBJEXT) +ntfs_3g_OBJECTS = $(am_ntfs_3g_OBJECTS) +@ENABLE_NTFS_3G_TRUE@ntfs_3g_DEPENDENCIES = $(am__DEPENDENCIES_2) \ +@ENABLE_NTFS_3G_TRUE@ $(top_builddir)/libntfs-3g/libntfs-3g.la +ntfs_3g_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ntfs_3g_CFLAGS) \ + $(CFLAGS) $(ntfs_3g_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfs_3g_probe_SOURCES_DIST = ntfs-3g.probe.c +@ENABLE_NTFS_3G_TRUE@am_ntfs_3g_probe_OBJECTS = \ +@ENABLE_NTFS_3G_TRUE@ ntfs_3g_probe-ntfs-3g.probe.$(OBJEXT) +ntfs_3g_probe_OBJECTS = $(am_ntfs_3g_probe_OBJECTS) +@ENABLE_NTFS_3G_TRUE@ntfs_3g_probe_DEPENDENCIES = \ +@ENABLE_NTFS_3G_TRUE@ $(top_builddir)/libntfs-3g/libntfs-3g.la +ntfs_3g_probe_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ntfs_3g_probe_CFLAGS) \ + $(CFLAGS) $(ntfs_3g_probe_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfs_3g_secaudit_SOURCES_DIST = secaudit.c +@ENABLE_NTFS_3G_TRUE@am_ntfs_3g_secaudit_OBJECTS = \ +@ENABLE_NTFS_3G_TRUE@ ntfs_3g_secaudit-secaudit.$(OBJEXT) +ntfs_3g_secaudit_OBJECTS = $(am_ntfs_3g_secaudit_OBJECTS) +@ENABLE_NTFS_3G_TRUE@ntfs_3g_secaudit_DEPENDENCIES = \ +@ENABLE_NTFS_3G_TRUE@ $(top_builddir)/libntfs-3g/libntfs-3g.la +ntfs_3g_secaudit_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(ntfs_3g_secaudit_CFLAGS) $(CFLAGS) \ + $(ntfs_3g_secaudit_LDFLAGS) $(LDFLAGS) -o $@ +am__ntfs_3g_usermap_SOURCES_DIST = usermap.c +@ENABLE_NTFS_3G_TRUE@am_ntfs_3g_usermap_OBJECTS = \ +@ENABLE_NTFS_3G_TRUE@ ntfs_3g_usermap-usermap.$(OBJEXT) +ntfs_3g_usermap_OBJECTS = $(am_ntfs_3g_usermap_OBJECTS) +@ENABLE_NTFS_3G_TRUE@ntfs_3g_usermap_DEPENDENCIES = \ +@ENABLE_NTFS_3G_TRUE@ $(top_builddir)/libntfs-3g/libntfs-3g.la +ntfs_3g_usermap_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(ntfs_3g_usermap_CFLAGS) $(CFLAGS) $(ntfs_3g_usermap_LDFLAGS) \ + $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(lowntfs_3g_SOURCES) $(ntfs_3g_SOURCES) \ + $(ntfs_3g_probe_SOURCES) $(ntfs_3g_secaudit_SOURCES) \ + $(ntfs_3g_usermap_SOURCES) +DIST_SOURCES = $(am__lowntfs_3g_SOURCES_DIST) \ + $(am__ntfs_3g_SOURCES_DIST) $(am__ntfs_3g_probe_SOURCES_DIST) \ + $(am__ntfs_3g_secaudit_SOURCES_DIST) \ + $(am__ntfs_3g_usermap_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(man_MANS) +DATA = $(rootsbin_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FUSE_MODULE_CFLAGS = @FUSE_MODULE_CFLAGS@ +FUSE_MODULE_LIBS = @FUSE_MODULE_LIBS@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDCONFIG = @LDCONFIG@ +LDFLAGS = @LDFLAGS@ +LIBFUSE_LITE_CFLAGS = @LIBFUSE_LITE_CFLAGS@ +LIBFUSE_LITE_LIBS = @LIBFUSE_LITE_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBNTFS_3G_VERSION = @LIBNTFS_3G_VERSION@ +LIBNTFS_CPPFLAGS = @LIBNTFS_CPPFLAGS@ +LIBNTFS_LIBS = @LIBNTFS_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MKNTFS_CPPFLAGS = @MKNTFS_CPPFLAGS@ +MKNTFS_LIBS = @MKNTFS_LIBS@ +MV = @MV@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NTFSPROGS_STATIC_LIBS = @NTFSPROGS_STATIC_LIBS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +OUTPUT_FORMAT = @OUTPUT_FORMAT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +RM = @RM@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +all_includes = @all_includes@ +all_libraries = @all_libraries@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +ntfs3gincludedir = @ntfs3gincludedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rootbindir = @rootbindir@ +rootlibdir = @rootlibdir@ +rootsbindir = @rootsbindir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = secaudit.h ntfs-3g_common.h +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +@FUSE_INTERNAL_FALSE@FUSE_CFLAGS = $(FUSE_MODULE_CFLAGS) +@FUSE_INTERNAL_TRUE@FUSE_CFLAGS = -I$(top_srcdir)/include/fuse-lite +@FUSE_INTERNAL_FALSE@FUSE_LIBS = $(FUSE_MODULE_LIBS) +@FUSE_INTERNAL_TRUE@FUSE_LIBS = $(top_builddir)/libfuse-lite/libfuse-lite.la +@ENABLE_NTFS_3G_TRUE@rootsbin_DATA = #Create directory +@ENABLE_NTFS_3G_TRUE@man_MANS = ntfs-3g.8 ntfs-3g.probe.8 \ +@ENABLE_NTFS_3G_TRUE@ ntfs-3g.usermap.8 \ +@ENABLE_NTFS_3G_TRUE@ ntfs-3g.secaudit.8 + +@ENABLE_NTFS_3G_TRUE@ntfs_3g_LDADD = $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la +@ENABLE_NTFS_3G_TRUE@@REALLYSTATIC_TRUE@ntfs_3g_LDFLAGS = $(AM_LDFLAGS) -all-static +@ENABLE_NTFS_3G_TRUE@ntfs_3g_CFLAGS = \ +@ENABLE_NTFS_3G_TRUE@ $(AM_CFLAGS) \ +@ENABLE_NTFS_3G_TRUE@ -DFUSE_USE_VERSION=26 \ +@ENABLE_NTFS_3G_TRUE@ $(FUSE_CFLAGS) \ +@ENABLE_NTFS_3G_TRUE@ -I$(top_srcdir)/include/ntfs-3g + +@ENABLE_NTFS_3G_TRUE@ntfs_3g_SOURCES = ntfs-3g.c ntfs-3g_common.c +@ENABLE_NTFS_3G_TRUE@lowntfs_3g_LDADD = $(FUSE_LIBS) $(top_builddir)/libntfs-3g/libntfs-3g.la +@ENABLE_NTFS_3G_TRUE@@REALLYSTATIC_TRUE@lowntfs_3g_LDFLAGS = $(AM_LDFLAGS) -all-static +@ENABLE_NTFS_3G_TRUE@lowntfs_3g_CFLAGS = \ +@ENABLE_NTFS_3G_TRUE@ $(AM_CFLAGS) \ +@ENABLE_NTFS_3G_TRUE@ -DFUSE_USE_VERSION=26 \ +@ENABLE_NTFS_3G_TRUE@ $(FUSE_CFLAGS) \ +@ENABLE_NTFS_3G_TRUE@ -I$(top_srcdir)/include/ntfs-3g + +@ENABLE_NTFS_3G_TRUE@lowntfs_3g_SOURCES = lowntfs-3g.c ntfs-3g_common.c +@ENABLE_NTFS_3G_TRUE@ntfs_3g_probe_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la +@ENABLE_NTFS_3G_TRUE@ntfs_3g_usermap_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la +@ENABLE_NTFS_3G_TRUE@ntfs_3g_secaudit_LDADD = $(top_builddir)/libntfs-3g/libntfs-3g.la +@ENABLE_NTFS_3G_TRUE@@REALLYSTATIC_TRUE@ntfs_3g_probe_LDFLAGS = $(AM_LDFLAGS) -all-static +@ENABLE_NTFS_3G_TRUE@@REALLYSTATIC_TRUE@ntfs_3g_usermap_LDFLAGS = $(AM_LDFLAGS) -all-static +@ENABLE_NTFS_3G_TRUE@@REALLYSTATIC_TRUE@ntfs_3g_secaudit_LDFLAGS = $(AM_LDFLAGS) -all-static +@ENABLE_NTFS_3G_TRUE@ntfs_3g_probe_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g +@ENABLE_NTFS_3G_TRUE@ntfs_3g_usermap_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g +@ENABLE_NTFS_3G_TRUE@ntfs_3g_secaudit_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g +@ENABLE_NTFS_3G_TRUE@ntfs_3g_probe_SOURCES = ntfs-3g.probe.c +@ENABLE_NTFS_3G_TRUE@ntfs_3g_usermap_SOURCES = usermap.c +@ENABLE_NTFS_3G_TRUE@ntfs_3g_secaudit_SOURCES = secaudit.c +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +ntfs-3g.8: $(top_builddir)/config.status $(srcdir)/ntfs-3g.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfs-3g.probe.8: $(top_builddir)/config.status $(srcdir)/ntfs-3g.probe.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfs-3g.usermap.8: $(top_builddir)/config.status $(srcdir)/ntfs-3g.usermap.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ntfs-3g.secaudit.8: $(top_builddir)/config.status $(srcdir)/ntfs-3g.secaudit.8.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-rootbinPROGRAMS: $(rootbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(rootbin_PROGRAMS)'; test -n "$(rootbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(rootbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(rootbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(rootbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(rootbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-rootbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(rootbin_PROGRAMS)'; test -n "$(rootbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(rootbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(rootbindir)" && rm -f $$files + +clean-rootbinPROGRAMS: + @list='$(rootbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +lowntfs-3g$(EXEEXT): $(lowntfs_3g_OBJECTS) $(lowntfs_3g_DEPENDENCIES) $(EXTRA_lowntfs_3g_DEPENDENCIES) + @rm -f lowntfs-3g$(EXEEXT) + $(AM_V_CCLD)$(lowntfs_3g_LINK) $(lowntfs_3g_OBJECTS) $(lowntfs_3g_LDADD) $(LIBS) + +ntfs-3g$(EXEEXT): $(ntfs_3g_OBJECTS) $(ntfs_3g_DEPENDENCIES) $(EXTRA_ntfs_3g_DEPENDENCIES) + @rm -f ntfs-3g$(EXEEXT) + $(AM_V_CCLD)$(ntfs_3g_LINK) $(ntfs_3g_OBJECTS) $(ntfs_3g_LDADD) $(LIBS) + +ntfs-3g.probe$(EXEEXT): $(ntfs_3g_probe_OBJECTS) $(ntfs_3g_probe_DEPENDENCIES) $(EXTRA_ntfs_3g_probe_DEPENDENCIES) + @rm -f ntfs-3g.probe$(EXEEXT) + $(AM_V_CCLD)$(ntfs_3g_probe_LINK) $(ntfs_3g_probe_OBJECTS) $(ntfs_3g_probe_LDADD) $(LIBS) + +ntfs-3g.secaudit$(EXEEXT): $(ntfs_3g_secaudit_OBJECTS) $(ntfs_3g_secaudit_DEPENDENCIES) $(EXTRA_ntfs_3g_secaudit_DEPENDENCIES) + @rm -f ntfs-3g.secaudit$(EXEEXT) + $(AM_V_CCLD)$(ntfs_3g_secaudit_LINK) $(ntfs_3g_secaudit_OBJECTS) $(ntfs_3g_secaudit_LDADD) $(LIBS) + +ntfs-3g.usermap$(EXEEXT): $(ntfs_3g_usermap_OBJECTS) $(ntfs_3g_usermap_DEPENDENCIES) $(EXTRA_ntfs_3g_usermap_DEPENDENCIES) + @rm -f ntfs-3g.usermap$(EXEEXT) + $(AM_V_CCLD)$(ntfs_3g_usermap_LINK) $(ntfs_3g_usermap_OBJECTS) $(ntfs_3g_usermap_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lowntfs_3g-lowntfs-3g.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lowntfs_3g-ntfs-3g_common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfs_3g-ntfs-3g.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfs_3g-ntfs-3g_common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfs_3g_secaudit-secaudit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntfs_3g_usermap-usermap.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +lowntfs_3g-lowntfs-3g.o: lowntfs-3g.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -MT lowntfs_3g-lowntfs-3g.o -MD -MP -MF $(DEPDIR)/lowntfs_3g-lowntfs-3g.Tpo -c -o lowntfs_3g-lowntfs-3g.o `test -f 'lowntfs-3g.c' || echo '$(srcdir)/'`lowntfs-3g.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lowntfs_3g-lowntfs-3g.Tpo $(DEPDIR)/lowntfs_3g-lowntfs-3g.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lowntfs-3g.c' object='lowntfs_3g-lowntfs-3g.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -c -o lowntfs_3g-lowntfs-3g.o `test -f 'lowntfs-3g.c' || echo '$(srcdir)/'`lowntfs-3g.c + +lowntfs_3g-lowntfs-3g.obj: lowntfs-3g.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -MT lowntfs_3g-lowntfs-3g.obj -MD -MP -MF $(DEPDIR)/lowntfs_3g-lowntfs-3g.Tpo -c -o lowntfs_3g-lowntfs-3g.obj `if test -f 'lowntfs-3g.c'; then $(CYGPATH_W) 'lowntfs-3g.c'; else $(CYGPATH_W) '$(srcdir)/lowntfs-3g.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lowntfs_3g-lowntfs-3g.Tpo $(DEPDIR)/lowntfs_3g-lowntfs-3g.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lowntfs-3g.c' object='lowntfs_3g-lowntfs-3g.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -c -o lowntfs_3g-lowntfs-3g.obj `if test -f 'lowntfs-3g.c'; then $(CYGPATH_W) 'lowntfs-3g.c'; else $(CYGPATH_W) '$(srcdir)/lowntfs-3g.c'; fi` + +lowntfs_3g-ntfs-3g_common.o: ntfs-3g_common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -MT lowntfs_3g-ntfs-3g_common.o -MD -MP -MF $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Tpo -c -o lowntfs_3g-ntfs-3g_common.o `test -f 'ntfs-3g_common.c' || echo '$(srcdir)/'`ntfs-3g_common.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Tpo $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ntfs-3g_common.c' object='lowntfs_3g-ntfs-3g_common.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -c -o lowntfs_3g-ntfs-3g_common.o `test -f 'ntfs-3g_common.c' || echo '$(srcdir)/'`ntfs-3g_common.c + +lowntfs_3g-ntfs-3g_common.obj: ntfs-3g_common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -MT lowntfs_3g-ntfs-3g_common.obj -MD -MP -MF $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Tpo -c -o lowntfs_3g-ntfs-3g_common.obj `if test -f 'ntfs-3g_common.c'; then $(CYGPATH_W) 'ntfs-3g_common.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g_common.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Tpo $(DEPDIR)/lowntfs_3g-ntfs-3g_common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ntfs-3g_common.c' object='lowntfs_3g-ntfs-3g_common.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lowntfs_3g_CFLAGS) $(CFLAGS) -c -o lowntfs_3g-ntfs-3g_common.obj `if test -f 'ntfs-3g_common.c'; then $(CYGPATH_W) 'ntfs-3g_common.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g_common.c'; fi` + +ntfs_3g-ntfs-3g.o: ntfs-3g.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -MT ntfs_3g-ntfs-3g.o -MD -MP -MF $(DEPDIR)/ntfs_3g-ntfs-3g.Tpo -c -o ntfs_3g-ntfs-3g.o `test -f 'ntfs-3g.c' || echo '$(srcdir)/'`ntfs-3g.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfs_3g-ntfs-3g.Tpo $(DEPDIR)/ntfs_3g-ntfs-3g.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ntfs-3g.c' object='ntfs_3g-ntfs-3g.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -c -o ntfs_3g-ntfs-3g.o `test -f 'ntfs-3g.c' || echo '$(srcdir)/'`ntfs-3g.c + +ntfs_3g-ntfs-3g.obj: ntfs-3g.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -MT ntfs_3g-ntfs-3g.obj -MD -MP -MF $(DEPDIR)/ntfs_3g-ntfs-3g.Tpo -c -o ntfs_3g-ntfs-3g.obj `if test -f 'ntfs-3g.c'; then $(CYGPATH_W) 'ntfs-3g.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfs_3g-ntfs-3g.Tpo $(DEPDIR)/ntfs_3g-ntfs-3g.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ntfs-3g.c' object='ntfs_3g-ntfs-3g.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -c -o ntfs_3g-ntfs-3g.obj `if test -f 'ntfs-3g.c'; then $(CYGPATH_W) 'ntfs-3g.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g.c'; fi` + +ntfs_3g-ntfs-3g_common.o: ntfs-3g_common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -MT ntfs_3g-ntfs-3g_common.o -MD -MP -MF $(DEPDIR)/ntfs_3g-ntfs-3g_common.Tpo -c -o ntfs_3g-ntfs-3g_common.o `test -f 'ntfs-3g_common.c' || echo '$(srcdir)/'`ntfs-3g_common.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfs_3g-ntfs-3g_common.Tpo $(DEPDIR)/ntfs_3g-ntfs-3g_common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ntfs-3g_common.c' object='ntfs_3g-ntfs-3g_common.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -c -o ntfs_3g-ntfs-3g_common.o `test -f 'ntfs-3g_common.c' || echo '$(srcdir)/'`ntfs-3g_common.c + +ntfs_3g-ntfs-3g_common.obj: ntfs-3g_common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -MT ntfs_3g-ntfs-3g_common.obj -MD -MP -MF $(DEPDIR)/ntfs_3g-ntfs-3g_common.Tpo -c -o ntfs_3g-ntfs-3g_common.obj `if test -f 'ntfs-3g_common.c'; then $(CYGPATH_W) 'ntfs-3g_common.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g_common.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfs_3g-ntfs-3g_common.Tpo $(DEPDIR)/ntfs_3g-ntfs-3g_common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ntfs-3g_common.c' object='ntfs_3g-ntfs-3g_common.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_CFLAGS) $(CFLAGS) -c -o ntfs_3g-ntfs-3g_common.obj `if test -f 'ntfs-3g_common.c'; then $(CYGPATH_W) 'ntfs-3g_common.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g_common.c'; fi` + +ntfs_3g_probe-ntfs-3g.probe.o: ntfs-3g.probe.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_probe_CFLAGS) $(CFLAGS) -MT ntfs_3g_probe-ntfs-3g.probe.o -MD -MP -MF $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Tpo -c -o ntfs_3g_probe-ntfs-3g.probe.o `test -f 'ntfs-3g.probe.c' || echo '$(srcdir)/'`ntfs-3g.probe.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Tpo $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ntfs-3g.probe.c' object='ntfs_3g_probe-ntfs-3g.probe.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_probe_CFLAGS) $(CFLAGS) -c -o ntfs_3g_probe-ntfs-3g.probe.o `test -f 'ntfs-3g.probe.c' || echo '$(srcdir)/'`ntfs-3g.probe.c + +ntfs_3g_probe-ntfs-3g.probe.obj: ntfs-3g.probe.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_probe_CFLAGS) $(CFLAGS) -MT ntfs_3g_probe-ntfs-3g.probe.obj -MD -MP -MF $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Tpo -c -o ntfs_3g_probe-ntfs-3g.probe.obj `if test -f 'ntfs-3g.probe.c'; then $(CYGPATH_W) 'ntfs-3g.probe.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g.probe.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Tpo $(DEPDIR)/ntfs_3g_probe-ntfs-3g.probe.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ntfs-3g.probe.c' object='ntfs_3g_probe-ntfs-3g.probe.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_probe_CFLAGS) $(CFLAGS) -c -o ntfs_3g_probe-ntfs-3g.probe.obj `if test -f 'ntfs-3g.probe.c'; then $(CYGPATH_W) 'ntfs-3g.probe.c'; else $(CYGPATH_W) '$(srcdir)/ntfs-3g.probe.c'; fi` + +ntfs_3g_secaudit-secaudit.o: secaudit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_secaudit_CFLAGS) $(CFLAGS) -MT ntfs_3g_secaudit-secaudit.o -MD -MP -MF $(DEPDIR)/ntfs_3g_secaudit-secaudit.Tpo -c -o ntfs_3g_secaudit-secaudit.o `test -f 'secaudit.c' || echo '$(srcdir)/'`secaudit.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfs_3g_secaudit-secaudit.Tpo $(DEPDIR)/ntfs_3g_secaudit-secaudit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='secaudit.c' object='ntfs_3g_secaudit-secaudit.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_secaudit_CFLAGS) $(CFLAGS) -c -o ntfs_3g_secaudit-secaudit.o `test -f 'secaudit.c' || echo '$(srcdir)/'`secaudit.c + +ntfs_3g_secaudit-secaudit.obj: secaudit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_secaudit_CFLAGS) $(CFLAGS) -MT ntfs_3g_secaudit-secaudit.obj -MD -MP -MF $(DEPDIR)/ntfs_3g_secaudit-secaudit.Tpo -c -o ntfs_3g_secaudit-secaudit.obj `if test -f 'secaudit.c'; then $(CYGPATH_W) 'secaudit.c'; else $(CYGPATH_W) '$(srcdir)/secaudit.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfs_3g_secaudit-secaudit.Tpo $(DEPDIR)/ntfs_3g_secaudit-secaudit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='secaudit.c' object='ntfs_3g_secaudit-secaudit.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_secaudit_CFLAGS) $(CFLAGS) -c -o ntfs_3g_secaudit-secaudit.obj `if test -f 'secaudit.c'; then $(CYGPATH_W) 'secaudit.c'; else $(CYGPATH_W) '$(srcdir)/secaudit.c'; fi` + +ntfs_3g_usermap-usermap.o: usermap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_usermap_CFLAGS) $(CFLAGS) -MT ntfs_3g_usermap-usermap.o -MD -MP -MF $(DEPDIR)/ntfs_3g_usermap-usermap.Tpo -c -o ntfs_3g_usermap-usermap.o `test -f 'usermap.c' || echo '$(srcdir)/'`usermap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfs_3g_usermap-usermap.Tpo $(DEPDIR)/ntfs_3g_usermap-usermap.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='usermap.c' object='ntfs_3g_usermap-usermap.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_usermap_CFLAGS) $(CFLAGS) -c -o ntfs_3g_usermap-usermap.o `test -f 'usermap.c' || echo '$(srcdir)/'`usermap.c + +ntfs_3g_usermap-usermap.obj: usermap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_usermap_CFLAGS) $(CFLAGS) -MT ntfs_3g_usermap-usermap.obj -MD -MP -MF $(DEPDIR)/ntfs_3g_usermap-usermap.Tpo -c -o ntfs_3g_usermap-usermap.obj `if test -f 'usermap.c'; then $(CYGPATH_W) 'usermap.c'; else $(CYGPATH_W) '$(srcdir)/usermap.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntfs_3g_usermap-usermap.Tpo $(DEPDIR)/ntfs_3g_usermap-usermap.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='usermap.c' object='ntfs_3g_usermap-usermap.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntfs_3g_usermap_CFLAGS) $(CFLAGS) -c -o ntfs_3g_usermap-usermap.obj `if test -f 'usermap.c'; then $(CYGPATH_W) 'usermap.c'; else $(CYGPATH_W) '$(srcdir)/usermap.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man8: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man8dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.8[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ + done; } + +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man8dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) +install-rootsbinDATA: $(rootsbin_DATA) + @$(NORMAL_INSTALL) + @list='$(rootsbin_DATA)'; test -n "$(rootsbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(rootsbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(rootsbindir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(rootsbindir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(rootsbindir)" || exit $$?; \ + done + +uninstall-rootsbinDATA: + @$(NORMAL_UNINSTALL) + @list='$(rootsbin_DATA)'; test -n "$(rootsbindir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(rootsbindir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(MANS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(rootbindir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(rootsbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +@ENABLE_MOUNT_HELPER_FALSE@install-exec-local: +@ENABLE_NTFS_3G_FALSE@install-exec-local: +@ENABLE_NTFS_3G_FALSE@install-data-local: +@ENABLE_NTFS_3G_FALSE@uninstall-local: +@ENABLE_NTFS_3G_FALSE@install-exec-hook: +@RUN_LDCONFIG_FALSE@install-exec-hook: +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool \ + clean-rootbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-data-local install-man \ + install-rootbinPROGRAMS install-rootsbinDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-exec-local + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man8 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-local uninstall-man \ + uninstall-rootbinPROGRAMS uninstall-rootsbinDATA + +uninstall-man: uninstall-man8 + +.MAKE: install-am install-exec-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool \ + clean-rootbinPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-data-local install-dvi install-dvi-am \ + install-exec install-exec-am install-exec-hook \ + install-exec-local install-html install-html-am install-info \ + install-info-am install-man install-man8 install-pdf \ + install-pdf-am install-ps install-ps-am \ + install-rootbinPROGRAMS install-rootsbinDATA install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ + uninstall-local uninstall-man uninstall-man8 \ + uninstall-rootbinPROGRAMS uninstall-rootsbinDATA + + +@ENABLE_NTFS_3G_TRUE@drivers : $(FUSE_LIBS) ntfs-3g lowntfs-3g + +@ENABLE_NTFS_3G_TRUE@@RUN_LDCONFIG_TRUE@install-exec-hook: +@ENABLE_NTFS_3G_TRUE@@RUN_LDCONFIG_TRUE@ $(LDCONFIG) + +@ENABLE_MOUNT_HELPER_TRUE@@ENABLE_NTFS_3G_TRUE@install-exec-local: install-rootbinPROGRAMS +@ENABLE_MOUNT_HELPER_TRUE@@ENABLE_NTFS_3G_TRUE@ $(MKDIR_P) "$(DESTDIR)/sbin" +@ENABLE_MOUNT_HELPER_TRUE@@ENABLE_NTFS_3G_TRUE@ $(LN_S) -f "$(rootbindir)/ntfs-3g" "$(DESTDIR)/sbin/mount.ntfs-3g" +@ENABLE_MOUNT_HELPER_TRUE@@ENABLE_NTFS_3G_TRUE@ $(LN_S) -f "$(rootbindir)/lowntfs-3g" "$(DESTDIR)/sbin/mount.lowntfs-3g" + +@ENABLE_NTFS_3G_TRUE@install-data-local: install-man8 +@ENABLE_NTFS_3G_TRUE@ $(LN_S) -f ntfs-3g.8 "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8" +@ENABLE_NTFS_3G_TRUE@ $(LN_S) -f ntfs-3g.8 "$(DESTDIR)$(man8dir)/mount.lowntfs-3g.8" + +@ENABLE_NTFS_3G_TRUE@uninstall-local: +@ENABLE_NTFS_3G_TRUE@ $(RM) -f "$(DESTDIR)$(man8dir)/mount.ntfs-3g.8" +@ENABLE_MOUNT_HELPER_TRUE@@ENABLE_NTFS_3G_TRUE@ $(RM) -f "$(DESTDIR)/sbin/mount.ntfs-3g" "$(DESTDIR)/sbin/mount.lowntfs-3g" + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c new file mode 100755 index 0000000000000000000000000000000000000000..3ff8b9de4cd20b4fbc05a3792b8e660bda261416 --- /dev/null +++ b/src/lowntfs-3g.c @@ -0,0 +1,4191 @@ +/** + * ntfs-3g - Third Generation NTFS Driver + * + * Copyright (c) 2005-2007 Yura Pakhuchiy + * Copyright (c) 2005 Yuval Fledel + * Copyright (c) 2006-2009 Szabolcs Szakacsits + * Copyright (c) 2007-2015 Jean-Pierre Andre + * Copyright (c) 2009 Erik Larsson + * + * This file is originated from the Linux-NTFS project. + * + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include <fuse.h> +#include <fuse_lowlevel.h> + +#if !defined(FUSE_VERSION) || (FUSE_VERSION < 26) +#error "***********************************************************" +#error "* *" +#error "* Compilation requires at least FUSE version 2.6.0! *" +#error "* *" +#error "***********************************************************" +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#include <signal.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#include <syslog.h> +#include <sys/wait.h> + +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_MKDEV_H +#include <sys/mkdev.h> +#endif + +#if defined(__APPLE__) || defined(__DARWIN__) +#include <sys/dirent.h> +#elif defined(__sun) && defined (__SVR4) +#include <sys/param.h> +#endif /* defined(__APPLE__) || defined(__DARWIN__), ... */ + +#ifdef HAVE_LINUX_FS_H +#include <linux/fs.h> +#endif + +#include "compat.h" +#include "bitmap.h" +#include "attrib.h" +#include "inode.h" +#include "volume.h" +#include "dir.h" +#include "unistr.h" +#include "layout.h" +#include "index.h" +#include "ntfstime.h" +#include "security.h" +#include "reparse.h" +#include "object_id.h" +#include "efs.h" +#include "logging.h" +#include "xattrs.h" +#include "misc.h" +#include "ioctl.h" + +#include "ntfs-3g_common.h" + +/* + * The following permission checking modes are governed by + * the LPERMSCONFIG value in param.h + */ + +/* ACLS may be checked by kernel (requires a fuse patch) or here */ +#define KERNELACLS ((LPERMSCONFIG > 6) & (LPERMSCONFIG < 10)) +/* basic permissions may be checked by kernel or here */ +#define KERNELPERMS (((LPERMSCONFIG - 1) % 6) < 3) +/* may want to use fuse/kernel cacheing */ +#define CACHEING (!(LPERMSCONFIG % 3)) + +#if KERNELACLS & !KERNELPERMS +#error "Incompatible options KERNELACLS and KERNELPERMS" +#endif + +#if CACHEING & (KERNELACLS | !KERNELPERMS) +#warning "Fuse cacheing is only usable with basic permissions checked by kernel" +#endif + +#if !CACHEING +#define ATTR_TIMEOUT 0.0 +#define ENTRY_TIMEOUT 0.0 +#else + /* + * FUSE cacheing is only usable with basic permissions + * checked by the kernel with external fuse >= 2.8 + */ +#define ATTR_TIMEOUT (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 1.0 : 0.0) +#define ENTRY_TIMEOUT (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 1.0 : 0.0) +#endif +#define GHOSTLTH 40 /* max length of a ghost file name - see ghostformat */ + + /* sometimes the kernel cannot check access */ +#define ntfs_real_allowed_access(scx, ni, type) ntfs_allowed_access(scx, ni, type) +#if POSIXACLS & KERNELPERMS & !KERNELACLS + /* short-circuit if PERMS checked by kernel and ACLs by fs */ +#define ntfs_allowed_access(scx, ni, type) \ + ((scx)->vol->secure_flags & (1 << SECURITY_DEFAULT) \ + ? 1 : ntfs_allowed_access(scx, ni, type)) +#endif + +#define set_archive(ni) (ni)->flags |= FILE_ATTR_ARCHIVE +#define INODE(ino) ((ino) == 1 ? (MFT_REF)FILE_root : (MFT_REF)(ino)) + +typedef enum { + FSTYPE_NONE, + FSTYPE_UNKNOWN, + FSTYPE_FUSE, + FSTYPE_FUSEBLK +} fuse_fstype; + +typedef struct fill_item { + struct fill_item *next; + size_t bufsize; + size_t off; + char buf[0]; +} ntfs_fuse_fill_item_t; + +typedef struct fill_context { + struct fill_item *first; + struct fill_item *last; + fuse_req_t req; + fuse_ino_t ino; + BOOL filled; +} ntfs_fuse_fill_context_t; + +struct open_file { + struct open_file *next; + struct open_file *previous; + long long ghost; + fuse_ino_t ino; + fuse_ino_t parent; + int state; +} ; + +enum { + CLOSE_GHOST = 1, + CLOSE_COMPRESSED = 2, + CLOSE_ENCRYPTED = 4, + CLOSE_DMTIME = 8 +}; + +enum RM_TYPES { + RM_LINK, + RM_DIR, + RM_ANY, +} ; + +static struct ntfs_options opts; + +const char *EXEC_NAME = "lowntfs-3g"; + +static ntfs_fuse_context_t *ctx; +static u32 ntfs_sequence; +static const char ghostformat[] = ".ghost-ntfs-3g-%020llu"; + +static const char *usage_msg = +"\n" +"%s %s %s %d - Third Generation NTFS Driver\n" +"\t\tConfiguration type %d, " +#ifdef HAVE_SETXATTR +"XATTRS are on, " +#else +"XATTRS are off, " +#endif +#if POSIXACLS +"POSIX ACLS are on\n" +#else +"POSIX ACLS are off\n" +#endif +"\n" +"Copyright (C) 2005-2007 Yura Pakhuchiy\n" +"Copyright (C) 2006-2009 Szabolcs Szakacsits\n" +"Copyright (C) 2007-2015 Jean-Pierre Andre\n" +"Copyright (C) 2009 Erik Larsson\n" +"\n" +"Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n" +"\n" +"Options: ro (read-only mount), windows_names, uid=, gid=,\n" +" umask=, fmask=, dmask=, streams_interface=.\n" +" Please see the details in the manual (type: man ntfs-3g).\n" +"\n" +"Example: ntfs-3g /dev/sda1 /mnt/windows\n" +"\n" +"%s"; + +static const char ntfs_bad_reparse[] = "unsupported reparse point"; + +#ifdef FUSE_INTERNAL +int drop_privs(void); +int restore_privs(void); +#else +/* + * setuid and setgid root ntfs-3g denies to start with external FUSE, + * therefore the below functions are no-op in such case. + */ +static int drop_privs(void) { return 0; } +#if defined(linux) || defined(__uClinux__) +static int restore_privs(void) { return 0; } +#endif + +static const char *setuid_msg = +"Mount is denied because setuid and setgid root ntfs-3g is insecure with the\n" +"external FUSE library. Either remove the setuid/setgid bit from the binary\n" +"or rebuild NTFS-3G with integrated FUSE support and make it setuid root.\n" +"Please see more information at\n" +"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; + +static const char *unpriv_fuseblk_msg = +"Unprivileged user can not mount NTFS block devices using the external FUSE\n" +"library. Either mount the volume as root, or rebuild NTFS-3G with integrated\n" +"FUSE support and make it setuid root. Please see more information at\n" +"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; +#endif + + +static void ntfs_fuse_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) +{ + if (ctx->atime == ATIME_DISABLED) + mask &= ~NTFS_UPDATE_ATIME; + else if (ctx->atime == ATIME_RELATIVE && mask == NTFS_UPDATE_ATIME && + (le64_to_cpu(ni->last_access_time) + >= le64_to_cpu(ni->last_data_change_time)) && + (le64_to_cpu(ni->last_access_time) + >= le64_to_cpu(ni->last_mft_change_time))) + return; + ntfs_inode_update_times(ni, mask); +} + +static s64 ntfs_get_nr_free_mft_records(ntfs_volume *vol) +{ + ntfs_attr *na = vol->mftbmp_na; + s64 nr_free = ntfs_attr_get_free_bits(na); + + if (nr_free >= 0) + nr_free += (na->allocated_size - na->data_size) << 3; + return nr_free; +} + +/* + * Fill a security context as needed by security functions + * returns TRUE if there is a user mapping, + * FALSE if there is none + * This is not an error and the context is filled anyway, + * it is used for implicit Windows-like inheritance + */ + +static BOOL ntfs_fuse_fill_security_context(fuse_req_t req, + struct SECURITY_CONTEXT *scx) +{ + const struct fuse_ctx *fusecontext; + + scx->vol = ctx->vol; + scx->mapping[MAPUSERS] = ctx->security.mapping[MAPUSERS]; + scx->mapping[MAPGROUPS] = ctx->security.mapping[MAPGROUPS]; + scx->pseccache = &ctx->seccache; + if (req) { + fusecontext = fuse_req_ctx(req); + scx->uid = fusecontext->uid; + scx->gid = fusecontext->gid; + scx->tid = fusecontext->pid; +#ifdef FUSE_CAP_DONT_MASK + /* the umask can be processed by the file system */ + scx->umask = fusecontext->umask; +#else + /* the umask if forced by fuse on creation */ + scx->umask = 0; +#endif + + } else { + scx->uid = 0; + scx->gid = 0; + scx->tid = 0; + scx->umask = 0; + } + return (ctx->security.mapping[MAPUSERS] != (struct MAPPING*)NULL); +} + +static u64 ntfs_fuse_inode_lookup(fuse_ino_t parent, const char *name) +{ + u64 ino = (u64)-1; + u64 inum; + ntfs_inode *dir_ni; + + /* Open target directory. */ + dir_ni = ntfs_inode_open(ctx->vol, INODE(parent)); + if (dir_ni) { + /* Lookup file */ + inum = ntfs_inode_lookup_by_mbsname(dir_ni, name); + /* never return inodes 0 and 1 */ + if (MREF(inum) <= 1) { + inum = (u64)-1; + errno = ENOENT; + } + if (ntfs_inode_close(dir_ni) + || (inum == (u64)-1)) + ino = (u64)-1; + else + ino = MREF(inum); + } + return (ino); +} + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + +/* + * Check access to parent directory + * + * file inode is only opened when not fed in and S_ISVTX is requested, + * when already open and S_ISVTX, it *HAS TO* be fed in. + * + * returns 1 if allowed, + * 0 if not allowed or some error occurred (errno tells why) + */ + +static int ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, + ntfs_inode *dir_ni, fuse_ino_t ino, + ntfs_inode *ni, mode_t accesstype) +{ + int allowed; + ntfs_inode *ni2; + struct stat stbuf; + + allowed = ntfs_allowed_access(scx, dir_ni, accesstype); + /* + * for an not-owned sticky directory, have to + * check whether file itself is owned + */ + if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX)) + && (allowed == 2)) { + if (ni) + ni2 = ni; + else + ni2 = ntfs_inode_open(ctx->vol, INODE(ino)); + allowed = 0; + if (ni2) { + allowed = (ntfs_get_owner_mode(scx,ni2,&stbuf) >= 0) + && (stbuf.st_uid == scx->uid); + if (!ni) + ntfs_inode_close(ni2); + } + } + return (allowed); +} + +#endif /* !KERNELPERMS | (POSIXACLS & !KERNELACLS) */ + +/** + * ntfs_fuse_statfs - return information about mounted NTFS volume + * @path: ignored (but fuse requires it) + * @sfs: statfs structure in which to return the information + * + * Return information about the mounted NTFS volume @sb in the statfs structure + * pointed to by @sfs (this is initialized with zeros before ntfs_statfs is + * called). We interpret the values to be correct of the moment in time at + * which we are called. Most values are variable otherwise and this isn't just + * the free values but the totals as well. For example we can increase the + * total number of file nodes if we run out and we can keep doing this until + * there is no more space on the volume left at all. + * + * This code based on ntfs_statfs from ntfs kernel driver. + * + * Returns 0 on success or -errno on error. + */ + +static void ntfs_fuse_statfs(fuse_req_t req, + fuse_ino_t ino __attribute__((unused))) +{ + struct statvfs sfs; + s64 size; + int delta_bits; + ntfs_volume *vol; + + vol = ctx->vol; + if (vol) { + /* + * File system block size. Used to calculate used/free space by df. + * Incorrectly documented as "optimal transfer block size". + */ + sfs.f_bsize = vol->cluster_size; + + /* Fundamental file system block size, used as the unit. */ + sfs.f_frsize = vol->cluster_size; + + /* + * Total number of blocks on file system in units of f_frsize. + * Since inodes are also stored in blocks ($MFT is a file) hence + * this is the number of clusters on the volume. + */ + sfs.f_blocks = vol->nr_clusters; + + /* Free blocks available for all and for non-privileged processes. */ + size = vol->free_clusters; + if (size < 0) + size = 0; + sfs.f_bavail = sfs.f_bfree = size; + + /* Free inodes on the free space */ + delta_bits = vol->cluster_size_bits - vol->mft_record_size_bits; + if (delta_bits >= 0) + size <<= delta_bits; + else + size >>= -delta_bits; + + /* Number of inodes at this point in time. */ + sfs.f_files = (vol->mftbmp_na->allocated_size << 3) + size; + + /* Free inodes available for all and for non-privileged processes. */ + size += vol->free_mft_records; + if (size < 0) + size = 0; + sfs.f_ffree = sfs.f_favail = size; + + /* Maximum length of filenames. */ + sfs.f_namemax = NTFS_MAX_NAME_LEN; + fuse_reply_statfs(req, &sfs); + } else + fuse_reply_err(req, ENODEV); + +} + +static void set_fuse_error(int *err) +{ + if (!*err) + *err = -errno; +} + +#if 0 && (defined(__APPLE__) || defined(__DARWIN__)) /* Unfinished. */ +static int ntfs_macfuse_getxtimes(const char *org_path, + struct timespec *bkuptime, struct timespec *crtime) +{ + int res = 0; + ntfs_inode *ni; + char *path = NULL; + ntfschar *stream_name; + int stream_name_len; + + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); + if (stream_name_len < 0) + return stream_name_len; + memset(bkuptime, 0, sizeof(struct timespec)); + memset(crtime, 0, sizeof(struct timespec)); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) { + res = -errno; + goto exit; + } + + /* We have no backup timestamp in NTFS. */ + crtime->tv_sec = ni->creation_time; +exit: + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + free(path); + if (stream_name_len) + free(stream_name); + return res; +} + +int ntfs_macfuse_setcrtime(const char *path, const struct timespec *tv) +{ + ntfs_inode *ni; + int res = 0; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + if (tv) { + ni->creation_time = tv->tv_sec; + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + } + + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +int ntfs_macfuse_setbkuptime(const char *path, const struct timespec *tv) +{ + ntfs_inode *ni; + int res = 0; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + /* + * Only pretending to set backup time successfully to please the APIs of + * Mac OS X. In reality, NTFS has no backup time. + */ + + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +int ntfs_macfuse_setchgtime(const char *path, const struct timespec *tv) +{ + ntfs_inode *ni; + int res = 0; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + if (tv) { + ni->last_mft_change_time = tv->tv_sec; + ntfs_fuse_update_times(ni, 0); + } + + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + +static void ntfs_init(void *userdata __attribute__((unused)), + struct fuse_conn_info *conn) +{ +#if defined(__APPLE__) || defined(__DARWIN__) + FUSE_ENABLE_XTIMES(conn); +#endif +#ifdef FUSE_CAP_DONT_MASK + /* request umask not to be enforced by fuse */ + conn->want |= FUSE_CAP_DONT_MASK; +#endif /* defined FUSE_CAP_DONT_MASK */ +#ifdef FUSE_CAP_BIG_WRITES + if (ctx->big_writes + && ((ctx->vol->nr_clusters << ctx->vol->cluster_size_bits) + >= SAFE_CAPACITY_FOR_BIG_WRITES)) + conn->want |= FUSE_CAP_BIG_WRITES; +#endif +#ifdef FUSE_CAP_IOCTL_DIR + conn->want |= FUSE_CAP_IOCTL_DIR; +#endif /* defined(FUSE_CAP_IOCTL_DIR) */ +} + +static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, struct stat *stbuf) +{ + int res = 0; + ntfs_attr *na; + BOOL withusermapping; + + memset(stbuf, 0, sizeof(struct stat)); + withusermapping = (scx->mapping[MAPUSERS] != (struct MAPPING*)NULL); + if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + || (ni->flags & FILE_ATTR_REPARSE_POINT)) { + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + char *target; + int attr_size; + + errno = 0; + target = ntfs_make_symlink(ni, ctx->abs_mnt_point, + &attr_size); + /* + * If the reparse point is not a valid + * directory junction, and there is no error + * we still display as a symlink + */ + if (target || (errno == EOPNOTSUPP)) { + /* returning attribute size */ + if (target) + stbuf->st_size = attr_size; + else + stbuf->st_size = + sizeof(ntfs_bad_reparse); + stbuf->st_blocks = + (ni->allocated_size + 511) >> 9; + stbuf->st_nlink = + le16_to_cpu(ni->mrec->link_count); + stbuf->st_mode = S_IFLNK; + free(target); + } else { + res = -errno; + goto exit; + } + } else { + /* Directory. */ + stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask); + /* get index size, if not known */ + if (!test_nino_flag(ni, KnownSize)) { + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, + NTFS_INDEX_I30, 4); + if (na) { + ni->data_size = na->data_size; + ni->allocated_size = na->allocated_size; + set_nino_flag(ni, KnownSize); + ntfs_attr_close(na); + } + } + stbuf->st_size = ni->data_size; + stbuf->st_blocks = ni->allocated_size >> 9; + stbuf->st_nlink = 1; /* Make find(1) work */ + } + } else { + /* Regular or Interix (INTX) file. */ + stbuf->st_mode = S_IFREG; + stbuf->st_size = ni->data_size; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + /* + * return data size rounded to next 512 byte boundary for + * encrypted files to include padding required for decryption + * also include 2 bytes for padding info + */ + if (ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED) + && ni->data_size) + stbuf->st_size = ((ni->data_size + 511) & ~511) + 2; +#endif /* HAVE_SETXATTR */ + /* + * Temporary fix to make ActiveSync work via Samba 3.0. + * See more on the ntfs-3g-devel list. + */ + stbuf->st_blocks = (ni->allocated_size + 511) >> 9; + stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count); + if (ni->flags & FILE_ATTR_SYSTEM) { + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + stbuf->st_ino = ni->mft_no; + goto nodata; + } + /* Check whether it's Interix FIFO or socket. */ + if (!(ni->flags & FILE_ATTR_HIDDEN)) { + /* FIFO. */ + if (na->data_size == 0) + stbuf->st_mode = S_IFIFO; + /* Socket link. */ + if (na->data_size == 1) + stbuf->st_mode = S_IFSOCK; + } + /* + * Check whether it's Interix symbolic link, block or + * character device. + */ + if ((u64)na->data_size <= sizeof(INTX_FILE_TYPES) + + sizeof(ntfschar) * PATH_MAX + && (u64)na->data_size > + sizeof(INTX_FILE_TYPES)) { + INTX_FILE *intx_file; + + intx_file = + (INTX_FILE*)ntfs_malloc(na->data_size); + if (!intx_file) { + res = -errno; + ntfs_attr_close(na); + goto exit; + } + if (ntfs_attr_pread(na, 0, na->data_size, + intx_file) != na->data_size) { + res = -errno; + free(intx_file); + ntfs_attr_close(na); + goto exit; + } + if (intx_file->magic == INTX_BLOCK_DEVICE && + na->data_size == (s64)offsetof( + INTX_FILE, device_end)) { + stbuf->st_mode = S_IFBLK; + stbuf->st_rdev = makedev(le64_to_cpu( + intx_file->major), + le64_to_cpu( + intx_file->minor)); + } + if (intx_file->magic == INTX_CHARACTER_DEVICE && + na->data_size == (s64)offsetof( + INTX_FILE, device_end)) { + stbuf->st_mode = S_IFCHR; + stbuf->st_rdev = makedev(le64_to_cpu( + intx_file->major), + le64_to_cpu( + intx_file->minor)); + } + if (intx_file->magic == INTX_SYMBOLIC_LINK) + stbuf->st_mode = S_IFLNK; + free(intx_file); + } + ntfs_attr_close(na); + } + stbuf->st_mode |= (0777 & ~ctx->fmask); + } + if (withusermapping) { + if (ntfs_get_owner_mode(scx,ni,stbuf) < 0) + set_fuse_error(&res); + } else { + stbuf->st_uid = ctx->uid; + stbuf->st_gid = ctx->gid; + } + if (S_ISLNK(stbuf->st_mode)) + stbuf->st_mode |= 0777; +nodata : + stbuf->st_ino = ni->mft_no; +#ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC + stbuf->st_atimespec = ntfs2timespec(ni->last_access_time); + stbuf->st_ctimespec = ntfs2timespec(ni->last_mft_change_time); + stbuf->st_mtimespec = ntfs2timespec(ni->last_data_change_time); +#elif defined(HAVE_STRUCT_STAT_ST_ATIM) + stbuf->st_atim = ntfs2timespec(ni->last_access_time); + stbuf->st_ctim = ntfs2timespec(ni->last_mft_change_time); + stbuf->st_mtim = ntfs2timespec(ni->last_data_change_time); +#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC) + { + struct timespec ts; + + ts = ntfs2timespec(ni->last_access_time); + stbuf->st_atime = ts.tv_sec; + stbuf->st_atimensec = ts.tv_nsec; + ts = ntfs2timespec(ni->last_mft_change_time); + stbuf->st_ctime = ts.tv_sec; + stbuf->st_ctimensec = ts.tv_nsec; + ts = ntfs2timespec(ni->last_data_change_time); + stbuf->st_mtime = ts.tv_sec; + stbuf->st_mtimensec = ts.tv_nsec; + } +#else +#warning "No known way to set nanoseconds in struct stat !" + { + struct timespec ts; + + ts = ntfs2timespec(ni->last_access_time); + stbuf->st_atime = ts.tv_sec; + ts = ntfs2timespec(ni->last_mft_change_time); + stbuf->st_ctime = ts.tv_sec; + ts = ntfs2timespec(ni->last_data_change_time); + stbuf->st_mtime = ts.tv_sec; + } +#endif +exit: + return (res); +} + +static void ntfs_fuse_getattr(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi __attribute__((unused))) +{ + int res; + ntfs_inode *ni; + struct stat stbuf; + struct SECURITY_CONTEXT security; + + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) + res = -errno; + else { + ntfs_fuse_fill_security_context(req, &security); + res = ntfs_fuse_getstat(&security, ni, &stbuf); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + if (!res) + fuse_reply_attr(req, &stbuf, ATTR_TIMEOUT); + else + fuse_reply_err(req, -res); +} + +static __inline__ BOOL ntfs_fuse_fillstat(struct SECURITY_CONTEXT *scx, + struct fuse_entry_param *pentry, u64 iref) +{ + ntfs_inode *ni; + BOOL ok = FALSE; + + pentry->ino = MREF(iref); + ni = ntfs_inode_open(ctx->vol, pentry->ino); + if (ni) { + if (!ntfs_fuse_getstat(scx, ni, &pentry->attr)) { + pentry->generation = 1; + pentry->attr_timeout = ATTR_TIMEOUT; + pentry->entry_timeout = ENTRY_TIMEOUT; + ok = TRUE; + } + if (ntfs_inode_close(ni)) + ok = FALSE; + } + return (ok); +} + + +static void ntfs_fuse_lookup(fuse_req_t req, fuse_ino_t parent, + const char *name) +{ + struct SECURITY_CONTEXT security; + struct fuse_entry_param entry; + ntfs_inode *dir_ni; + u64 iref; + BOOL ok = FALSE; + + if (strlen(name) < 256) { + dir_ni = ntfs_inode_open(ctx->vol, INODE(parent)); + if (dir_ni) { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* + * make sure the parent directory is searchable + */ + if (ntfs_fuse_fill_security_context(req, &security) + && !ntfs_allowed_access(&security,dir_ni,S_IEXEC)) { + ntfs_inode_close(dir_ni); + errno = EACCES; + } else { +#else + ntfs_fuse_fill_security_context(req, &security); +#endif + iref = ntfs_inode_lookup_by_mbsname(dir_ni, + name); + /* never return inodes 0 and 1 */ + if (MREF(iref) <= 1) { + iref = (u64)-1; + errno = ENOENT; + } + ok = !ntfs_inode_close(dir_ni) + && (iref != (u64)-1) + && ntfs_fuse_fillstat( + &security,&entry,iref); +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + } +#endif + } + } else + errno = ENAMETOOLONG; + if (!ok) + fuse_reply_err(req, errno); + else + fuse_reply_entry(req, &entry); +} + +static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino) +{ + ntfs_inode *ni = NULL; + ntfs_attr *na = NULL; + INTX_FILE *intx_file = NULL; + char *buf = (char*)NULL; + int res = 0; + + /* Get inode. */ + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + res = -errno; + goto exit; + } + /* + * Reparse point : analyze as a junction point + */ + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + int attr_size; + + errno = 0; + res = 0; + buf = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size); + if (!buf) { + if (errno == EOPNOTSUPP) + buf = strdup(ntfs_bad_reparse); + if (!buf) + res = -errno; + } + goto exit; + } + /* Sanity checks. */ + if (!(ni->flags & FILE_ATTR_SYSTEM)) { + res = -EINVAL; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + res = -errno; + goto exit; + } + if ((size_t)na->data_size <= sizeof(INTX_FILE_TYPES)) { + res = -EINVAL; + goto exit; + } + if ((size_t)na->data_size > sizeof(INTX_FILE_TYPES) + + sizeof(ntfschar) * PATH_MAX) { + res = -ENAMETOOLONG; + goto exit; + } + /* Receive file content. */ + intx_file = (INTX_FILE*)ntfs_malloc(na->data_size); + if (!intx_file) { + res = -errno; + goto exit; + } + if (ntfs_attr_pread(na, 0, na->data_size, intx_file) != na->data_size) { + res = -errno; + goto exit; + } + /* Sanity check. */ + if (intx_file->magic != INTX_SYMBOLIC_LINK) { + res = -EINVAL; + goto exit; + } + /* Convert link from unicode to local encoding. */ + if (ntfs_ucstombs(intx_file->target, (na->data_size - + offsetof(INTX_FILE, target)) / sizeof(ntfschar), + &buf, 0) < 0) { + res = -errno; + goto exit; + } +exit: + if (intx_file) + free(intx_file); + if (na) + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_readlink(req, buf); + if (buf != ntfs_bad_reparse) + free(buf); +} + +static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, + const ntfschar *name, const int name_len, const int name_type, + const s64 pos __attribute__((unused)), const MFT_REF mref, + const unsigned dt_type __attribute__((unused))) +{ + char *filename = NULL; + int ret = 0; + int filenamelen = -1; + size_t sz; + ntfs_fuse_fill_item_t *current; + ntfs_fuse_fill_item_t *newone; + + if (name_type == FILE_NAME_DOS) + return 0; + + if ((filenamelen = ntfs_ucstombs(name, name_len, &filename, 0)) < 0) { + ntfs_log_perror("Filename decoding failed (inode %llu)", + (unsigned long long)MREF(mref)); + return -1; + } + /* never return inodes 0 and 1 */ + if (MREF(mref) > 1) { + struct stat st = { .st_ino = MREF(mref) }; + + switch (dt_type) { + case NTFS_DT_DIR : + st.st_mode = S_IFDIR | (0777 & ~ctx->dmask); + break; + case NTFS_DT_LNK : + st.st_mode = S_IFLNK | 0777; + break; + case NTFS_DT_FIFO : + st.st_mode = S_IFIFO; + break; + case NTFS_DT_SOCK : + st.st_mode = S_IFSOCK; + break; + case NTFS_DT_BLK : + st.st_mode = S_IFBLK; + break; + case NTFS_DT_CHR : + st.st_mode = S_IFCHR; + break; + default : /* unexpected types shown as plain files */ + case NTFS_DT_REG : + st.st_mode = S_IFREG | (0777 & ~ctx->fmask); + break; + } + +#if defined(__APPLE__) || defined(__DARWIN__) + /* + * Returning file names larger than MAXNAMLEN (255) bytes + * causes Darwin/Mac OS X to bug out and skip the entry. + */ + if (filenamelen > MAXNAMLEN) { + ntfs_log_debug("Truncating %d byte filename to " + "%d bytes.\n", filenamelen, MAXNAMLEN); + ntfs_log_debug(" before: '%s'\n", filename); + memset(filename + MAXNAMLEN, 0, filenamelen - MAXNAMLEN); + ntfs_log_debug(" after: '%s'\n", filename); + } +#elif defined(__sun) && defined (__SVR4) + /* + * Returning file names larger than MAXNAMELEN (256) bytes + * causes Solaris/Illumos to return an I/O error from the system + * call. + * However we also need space for a terminating NULL, or user + * space tools will bug out since they expect a NULL terminator. + * Effectively the maximum length of a file name is MAXNAMELEN - + * 1 (255). + */ + if (filenamelen > (MAXNAMELEN - 1)) { + ntfs_log_debug("Truncating %d byte filename to %d " + "bytes.\n", filenamelen, MAXNAMELEN - 1); + ntfs_log_debug(" before: '%s'\n", filename); + memset(&filename[MAXNAMELEN - 1], 0, + filenamelen - (MAXNAMELEN - 1)); + ntfs_log_debug(" after: '%s'\n", filename); + } +#endif /* defined(__APPLE__) || defined(__DARWIN__), ... */ + + current = fill_ctx->last; + sz = fuse_add_direntry(fill_ctx->req, + ¤t->buf[current->off], + current->bufsize - current->off, + filename, &st, current->off); + if (!sz || ((current->off + sz) > current->bufsize)) { + newone = (ntfs_fuse_fill_item_t*)ntfs_malloc + (sizeof(ntfs_fuse_fill_item_t) + + current->bufsize); + if (newone) { + newone->off = 0; + newone->bufsize = current->bufsize; + newone->next = (ntfs_fuse_fill_item_t*)NULL; + current->next = newone; + fill_ctx->last = newone; + current = newone; + sz = fuse_add_direntry(fill_ctx->req, + current->buf, + current->bufsize - current->off, + filename, &st, current->off); + if (!sz) { + errno = EIO; + ntfs_log_error("Could not add a" + " directory entry (inode %lld)\n", + (unsigned long long)MREF(mref)); + } + } else { + sz = 0; + errno = ENOMEM; + } + } + if (sz) { + current->off += sz; + } else { + ret = -1; + } + } + + free(filename); + return ret; +} + +static void ntfs_fuse_opendir(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + int res = 0; + ntfs_inode *ni; + int accesstype; + ntfs_fuse_fill_context_t *fill; + struct SECURITY_CONTEXT security; + + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (ni) { + if (ntfs_fuse_fill_security_context(req, &security)) { + if (fi->flags & O_WRONLY) + accesstype = S_IWRITE; + else + if (fi->flags & O_RDWR) + accesstype = S_IWRITE | S_IREAD; + else + accesstype = S_IREAD; + if (!ntfs_allowed_access(&security,ni,accesstype)) + res = -EACCES; + } + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + if (!res) { + fill = (ntfs_fuse_fill_context_t*) + ntfs_malloc(sizeof(ntfs_fuse_fill_context_t)); + if (!fill) + res = -errno; + else { + fill->first = fill->last + = (ntfs_fuse_fill_item_t*)NULL; + fill->filled = FALSE; + fill->ino = ino; + } + fi->fh = (long)fill; + } + } else + res = -errno; + if (!res) + fuse_reply_open(req, fi); + else + fuse_reply_err(req, -res); +} + + +static void ntfs_fuse_releasedir(fuse_req_t req, + fuse_ino_t ino __attribute__((unused)), + struct fuse_file_info *fi) +{ + ntfs_fuse_fill_context_t *fill; + ntfs_fuse_fill_item_t *current; + + fill = (ntfs_fuse_fill_context_t*)(long)fi->fh; + if (fill && (fill->ino == ino)) { + /* make sure to clear results */ + current = fill->first; + while (current) { + current = current->next; + free(fill->first); + fill->first = current; + } + fill->ino = 0; + free(fill); + } + fuse_reply_err(req, 0); +} + +static void ntfs_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off __attribute__((unused)), + struct fuse_file_info *fi __attribute__((unused))) +{ + ntfs_fuse_fill_item_t *first; + ntfs_fuse_fill_item_t *current; + ntfs_fuse_fill_context_t *fill; + ntfs_inode *ni; + s64 pos = 0; + int err = 0; + + fill = (ntfs_fuse_fill_context_t*)(long)fi->fh; + if (fill && (fill->ino == ino)) { + if (!fill->filled) { + /* initial call : build the full list */ + first = (ntfs_fuse_fill_item_t*)ntfs_malloc + (sizeof(ntfs_fuse_fill_item_t) + size); + if (first) { + first->bufsize = size; + first->off = 0; + first->next = (ntfs_fuse_fill_item_t*)NULL; + fill->req = req; + fill->first = first; + fill->last = first; + ni = ntfs_inode_open(ctx->vol,INODE(ino)); + if (!ni) + err = -errno; + else { + if (ntfs_readdir(ni, &pos, fill, + (ntfs_filldir_t) + ntfs_fuse_filler)) + err = -errno; + fill->filled = TRUE; + ntfs_fuse_update_times(ni, + NTFS_UPDATE_ATIME); + if (ntfs_inode_close(ni)) + set_fuse_error(&err); + } + if (!err) + fuse_reply_buf(req, first->buf, + first->off); + /* reply sent, now must exit with no error */ + fill->first = first->next; + free(first); + } else + err = -errno; + } else { + /* subsequent call : return next non-empty buffer */ + current = fill->first; + while (current && !current->off) { + current = current->next; + free(fill->first); + fill->first = current; + } + if (current) { + fuse_reply_buf(req, current->buf, current->off); + fill->first = current->next; + free(current); + } else { + fuse_reply_buf(req, (char*)NULL, 0); + /* reply sent, now must exit with no error */ + } + } + } else { + errno = EIO; + err = -errno; + ntfs_log_error("Uninitialized fuse_readdir()\n"); + } + if (err) + fuse_reply_err(req, -err); +} + +static void ntfs_fuse_open(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + ntfs_inode *ni; + ntfs_attr *na; + struct open_file *of; + int state = 0; + char *path = NULL; + int res = 0; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + int accesstype; + struct SECURITY_CONTEXT security; +#endif + + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (ni) { + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (na) { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + if (ntfs_fuse_fill_security_context(req, &security)) { + if (fi->flags & O_WRONLY) + accesstype = S_IWRITE; + else + if (fi->flags & O_RDWR) + accesstype = S_IWRITE | S_IREAD; + else + accesstype = S_IREAD; + /* check whether requested access is allowed */ + if (!ntfs_allowed_access(&security, + ni,accesstype)) + res = -EACCES; + } +#endif + if ((res >= 0) + && (fi->flags & (O_WRONLY | O_RDWR))) { + /* mark a future need to compress the last chunk */ + if (na->data_flags & ATTR_COMPRESSION_MASK) + state |= CLOSE_COMPRESSED; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + /* mark a future need to fixup encrypted inode */ + if (ctx->efs_raw + && !(na->data_flags & ATTR_IS_ENCRYPTED) + && (ni->flags & FILE_ATTR_ENCRYPTED)) + state |= CLOSE_ENCRYPTED; +#endif /* HAVE_SETXATTR */ + /* mark a future need to update the mtime */ + if (ctx->dmtime) + state |= CLOSE_DMTIME; + /* deny opening metadata files for writing */ + if (ino < FILE_first_user) + res = -EPERM; + } + ntfs_attr_close(na); + } else + res = -errno; + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } else + res = -errno; + free(path); + if (res >= 0) { + of = (struct open_file*)malloc(sizeof(struct open_file)); + if (of) { + of->parent = 0; + of->ino = ino; + of->state = state; + of->next = ctx->open_files; + of->previous = (struct open_file*)NULL; + if (ctx->open_files) + ctx->open_files->previous = of; + ctx->open_files = of; + fi->fh = (long)of; + } + } + if (res) + fuse_reply_err(req, -res); + else + fuse_reply_open(req, fi); +} + +static void ntfs_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, + struct fuse_file_info *fi __attribute__((unused))) +{ + ntfs_inode *ni = NULL; + ntfs_attr *na = NULL; + int res; + char *buf = (char*)NULL; + s64 total = 0; + s64 max_read; + + if (!size) { + res = 0; + goto exit; + } + buf = (char*)ntfs_malloc(size); + if (!buf) { + res = -errno; + goto exit; + } + + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + res = -errno; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + res = -errno; + goto exit; + } + max_read = na->data_size; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + /* limit reads at next 512 byte boundary for encrypted attributes */ + if (ctx->efs_raw + && max_read + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) { + max_read = ((na->data_size+511) & ~511) + 2; + } +#endif /* HAVE_SETXATTR */ + if (offset + (off_t)size > max_read) { + if (max_read < offset) + goto ok; + size = max_read - offset; + } + while (size > 0) { + s64 ret = ntfs_attr_pread(na, offset, size, buf + total); + if (ret != (s64)size) + ntfs_log_perror("ntfs_attr_pread error reading inode %lld at " + "offset %lld: %lld <> %lld", (long long)ni->mft_no, + (long long)offset, (long long)size, (long long)ret); + if (ret <= 0 || ret > (s64)size) { + res = (ret < 0) ? -errno : -EIO; + goto exit; + } + size -= ret; + offset += ret; + total += ret; + } +ok: + ntfs_fuse_update_times(na->ni, NTFS_UPDATE_ATIME); + res = total; +exit: + if (na) + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_buf(req, buf, res); + free(buf); +} + +static void ntfs_fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, + size_t size, off_t offset, + struct fuse_file_info *fi __attribute__((unused))) +{ + ntfs_inode *ni = NULL; + ntfs_attr *na = NULL; + int res, total = 0; + + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + res = -errno; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + res = -errno; + goto exit; + } + while (size) { + s64 ret = ntfs_attr_pwrite(na, offset, size, buf + total); + if (ret <= 0) { + res = -errno; + goto exit; + } + size -= ret; + offset += ret; + total += ret; + } + res = total; + if ((res > 0) + && (!ctx->dmtime + || (le64_to_cpu(ntfs_current_time()) + - le64_to_cpu(ni->last_data_change_time)) > ctx->dmtime)) + ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME); +exit: + if (na) + ntfs_attr_close(na); + if (total) + set_archive(ni); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_write(req, res); +} + +static int ntfs_fuse_chmod(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, + mode_t mode, struct stat *stbuf) +{ + int res = 0; + ntfs_inode *ni; + + /* Unsupported if inherit or no user mapping has been defined */ + if ((!scx->mapping[MAPUSERS] || ctx->inherit) + && !ctx->silent) { + res = -EOPNOTSUPP; + } else { + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) + res = -errno; + else { + /* ignore if Windows inheritance is forced */ + if (scx->mapping[MAPUSERS] && !ctx->inherit) { + if (ntfs_set_mode(scx, ni, mode)) + res = -errno; + else { + ntfs_fuse_update_times(ni, + NTFS_UPDATE_CTIME); + /* + * Must return updated times, and + * inode has been updated, so hope + * we get no further errors + */ + res = ntfs_fuse_getstat(scx, ni, stbuf); + } + NInoSetDirty(ni); + } else + res = ntfs_fuse_getstat(scx, ni, stbuf); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } + return res; +} + +static int ntfs_fuse_chown(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, + uid_t uid, gid_t gid, struct stat *stbuf) +{ + ntfs_inode *ni; + int res; + + /* Unsupported if inherit or no user mapping has been defined */ + if ((!scx->mapping[MAPUSERS] || ctx->inherit) + && !ctx->silent + && ((uid != ctx->uid) || (gid != ctx->gid))) + res = -EOPNOTSUPP; + else { + res = 0; + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) + res = -errno; + else { + /* ignore if Windows inheritance is forced */ + if (scx->mapping[MAPUSERS] + && !ctx->inherit + && (((int)uid != -1) || ((int)gid != -1))) { + if (ntfs_set_owner(scx, ni, uid, gid)) + res = -errno; + else { + ntfs_fuse_update_times(ni, + NTFS_UPDATE_CTIME); + /* + * Must return updated times, and + * inode has been updated, so hope + * we get no further errors + */ + res = ntfs_fuse_getstat(scx, ni, stbuf); + } + } else + res = ntfs_fuse_getstat(scx, ni, stbuf); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } + return (res); +} + +static int ntfs_fuse_chownmod(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, + uid_t uid, gid_t gid, mode_t mode, struct stat *stbuf) +{ + ntfs_inode *ni; + int res; + + /* Unsupported if inherit or no user mapping has been defined */ + if ((!scx->mapping[MAPUSERS] || ctx->inherit) + && !ctx->silent + && ((uid != ctx->uid) || (gid != ctx->gid))) + res = -EOPNOTSUPP; + else { + res = 0; + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) + res = -errno; + else { + /* ignore if Windows inheritance is forced */ + if (scx->mapping[MAPUSERS] && !ctx->inherit) { + if (ntfs_set_ownmod(scx, ni, uid, gid, mode)) + res = -errno; + else { + ntfs_fuse_update_times(ni, + NTFS_UPDATE_CTIME); + /* + * Must return updated times, and + * inode has been updated, so hope + * we get no further errors + */ + res = ntfs_fuse_getstat(scx, ni, stbuf); + } + } else + res = ntfs_fuse_getstat(scx, ni, stbuf); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } + return (res); +} + +static int ntfs_fuse_trunc(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + off_t size, BOOL chkwrite, struct stat *stbuf) +#else + off_t size, BOOL chkwrite __attribute__((unused)), + struct stat *stbuf) +#endif +{ + ntfs_inode *ni = NULL; + ntfs_attr *na = NULL; + int res; + s64 oldsize; + + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) + goto exit; + + /* deny truncating metadata files */ + if (ino < FILE_first_user) { + errno = EPERM; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) + goto exit; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* + * deny truncation if cannot write to file + * (already checked for ftruncate()) + */ + if (scx->mapping[MAPUSERS] + && chkwrite + && !ntfs_allowed_access(scx, ni, S_IWRITE)) { + errno = EACCES; + goto exit; + } +#endif + /* + * for compressed files, upsizing is done by inserting a final + * zero, which is optimized as creating a hole when possible. + */ + oldsize = na->data_size; + if ((na->data_flags & ATTR_COMPRESSION_MASK) + && (size > na->initialized_size)) { + char zero = 0; + if (ntfs_attr_pwrite(na, size - 1, 1, &zero) <= 0) + goto exit; + } else + if (ntfs_attr_truncate(na, size)) + goto exit; + if (oldsize != size) + set_archive(ni); + + ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME); + res = ntfs_fuse_getstat(scx, ni, stbuf); + errno = (res ? -res : 0); +exit: + res = -errno; + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +#if defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) + +static int ntfs_fuse_utimens(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, + struct stat *stin, struct stat *stbuf, int to_set) +{ + ntfs_inode *ni; + int res = 0; + + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) + return -errno; + + /* no check or update if both UTIME_OMIT */ + if (to_set & (FUSE_SET_ATTR_ATIME + FUSE_SET_ATTR_MTIME)) { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + if (ntfs_allowed_as_owner(scx, ni) + || ((to_set & FUSE_SET_ATTR_ATIME_NOW) + && (to_set & FUSE_SET_ATTR_MTIME_NOW) + && ntfs_allowed_access(scx, ni, S_IWRITE))) { +#endif + ntfs_time_update_flags mask = NTFS_UPDATE_CTIME; + + if (to_set & FUSE_SET_ATTR_ATIME_NOW) + mask |= NTFS_UPDATE_ATIME; + else + if (to_set & FUSE_SET_ATTR_ATIME) + ni->last_access_time + = timespec2ntfs(stin->st_atim); + if (to_set & FUSE_SET_ATTR_MTIME_NOW) + mask |= NTFS_UPDATE_MTIME; + else + if (to_set & FUSE_SET_ATTR_MTIME) + ni->last_data_change_time + = timespec2ntfs(stin->st_mtim); + ntfs_inode_update_times(ni, mask); +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + } else + res = -errno; +#endif + } + if (!res) + res = ntfs_fuse_getstat(scx, ni, stbuf); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +#else /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */ + +static int ntfs_fuse_utime(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, + struct stat *stin, struct stat *stbuf) +{ + ntfs_inode *ni; + int res = 0; + struct timespec actime; + struct timespec modtime; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + BOOL ownerok; + BOOL writeok; +#endif + + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) + return -errno; + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + ownerok = ntfs_allowed_as_owner(scx, ni); + if (stin) { + /* + * fuse never calls with a NULL buf and we do not + * know whether the specific condition can be applied + * So we have to accept updating by a non-owner having + * write access. + */ + writeok = !ownerok + && (stin->st_atime == stin->st_mtime) + && ntfs_allowed_access(scx, ni, S_IWRITE); + /* Must be owner */ + if (!ownerok && !writeok) + res = (stin->st_atime == stin->st_mtime + ? -EACCES : -EPERM); + else { + actime.tv_sec = stin->st_atime; + actime.tv_nsec = 0; + modtime.tv_sec = stin->st_mtime; + modtime.tv_nsec = 0; + ni->last_access_time = timespec2ntfs(actime); + ni->last_data_change_time = timespec2ntfs(modtime); + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + } + } else { + /* Must be owner or have write access */ + writeok = !ownerok + && ntfs_allowed_access(scx, ni, S_IWRITE); + if (!ownerok && !writeok) + res = -EACCES; + else + ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME); + } +#else + if (stin) { + actime.tv_sec = stin->st_atime; + actime.tv_nsec = 0; + modtime.tv_sec = stin->st_mtime; + modtime.tv_nsec = 0; + ni->last_access_time = timespec2ntfs(actime); + ni->last_data_change_time = timespec2ntfs(modtime); + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + } else + ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME); +#endif + + res = ntfs_fuse_getstat(scx, ni, stbuf); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +#endif /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */ + +static void ntfs_fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int to_set, struct fuse_file_info *fi __attribute__((unused))) +{ + struct stat stbuf; + ntfs_inode *ni; + int res; + struct SECURITY_CONTEXT security; + + res = 0; + ntfs_fuse_fill_security_context(req, &security); + /* no flags */ + if (!(to_set + & (FUSE_SET_ATTR_MODE + | FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID + | FUSE_SET_ATTR_SIZE + | FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) { + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) + res = -errno; + else { + res = ntfs_fuse_getstat(&security, ni, &stbuf); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } + /* some set of uid/gid/mode */ + if (to_set + & (FUSE_SET_ATTR_MODE + | FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { + switch (to_set + & (FUSE_SET_ATTR_MODE + | FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { + case FUSE_SET_ATTR_MODE : + res = ntfs_fuse_chmod(&security, ino, + attr->st_mode & 07777, &stbuf); + break; + case FUSE_SET_ATTR_UID : + res = ntfs_fuse_chown(&security, ino, attr->st_uid, + (gid_t)-1, &stbuf); + break; + case FUSE_SET_ATTR_GID : + res = ntfs_fuse_chown(&security, ino, (uid_t)-1, + attr->st_gid, &stbuf); + break; + case FUSE_SET_ATTR_UID + FUSE_SET_ATTR_GID : + res = ntfs_fuse_chown(&security, ino, attr->st_uid, + attr->st_gid, &stbuf); + break; + case FUSE_SET_ATTR_UID + FUSE_SET_ATTR_MODE: + res = ntfs_fuse_chownmod(&security, ino, attr->st_uid, + (gid_t)-1,attr->st_mode, + &stbuf); + break; + case FUSE_SET_ATTR_GID + FUSE_SET_ATTR_MODE: + res = ntfs_fuse_chownmod(&security, ino, (uid_t)-1, + attr->st_gid,attr->st_mode, + &stbuf); + break; + case FUSE_SET_ATTR_UID + FUSE_SET_ATTR_GID + FUSE_SET_ATTR_MODE: + res = ntfs_fuse_chownmod(&security, ino, attr->st_uid, + attr->st_gid,attr->st_mode, &stbuf); + break; + default : + break; + } + } + /* size */ + if (!res && (to_set & FUSE_SET_ATTR_SIZE)) { + res = ntfs_fuse_trunc(&security, ino, attr->st_size, + !fi, &stbuf); + } + /* some set of atime/mtime */ + if (!res && (to_set & (FUSE_SET_ATTR_ATIME + FUSE_SET_ATTR_MTIME))) { +#if defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) + res = ntfs_fuse_utimens(&security, ino, attr, &stbuf, to_set); +#else /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */ + res = ntfs_fuse_utime(&security, ino, attr, &stbuf); +#endif /* defined(HAVE_UTIMENSAT) & defined(FUSE_SET_ATTR_ATIME_NOW) */ + } + if (res) + fuse_reply_err(req, -res); + else + fuse_reply_attr(req, &stbuf, ATTR_TIMEOUT); +} + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + +static void ntfs_fuse_access(fuse_req_t req, fuse_ino_t ino, int mask) +{ + int res = 0; + int mode; + ntfs_inode *ni; + struct SECURITY_CONTEXT security; + + /* JPA return unsupported if no user mapping has been defined */ + if (!ntfs_fuse_fill_security_context(req, &security)) { + if (ctx->silent) + res = 0; + else + res = -EOPNOTSUPP; + } else { + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + res = -errno; + } else { + mode = 0; + if (mask & (X_OK | W_OK | R_OK)) { + if (mask & X_OK) mode += S_IEXEC; + if (mask & W_OK) mode += S_IWRITE; + if (mask & R_OK) mode += S_IREAD; + if (!ntfs_allowed_access(&security, + ni, mode)) + res = -errno; + } + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_err(req, 0); +} + +#endif /* !KERNELPERMS | (POSIXACLS & !KERNELACLS) */ + +static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t typemode, dev_t dev, + struct fuse_entry_param *e, + const char *target, struct fuse_file_info *fi) +{ + ntfschar *uname = NULL, *utarget = NULL; + ntfs_inode *dir_ni = NULL, *ni; + struct open_file *of; + int state = 0; + le32 securid; + gid_t gid; + mode_t dsetgid; + mode_t type = typemode & ~07777; + mode_t perm; + struct SECURITY_CONTEXT security; + int res = 0, uname_len, utarget_len; + + /* Generate unicode filename. */ + uname_len = ntfs_mbstoucs(name, &uname); + if ((uname_len < 0) + || (ctx->windows_names + && ntfs_forbidden_names(ctx->vol,uname,uname_len))) { + res = -errno; + goto exit; + } + /* Open parent directory. */ + dir_ni = ntfs_inode_open(ctx->vol, INODE(parent)); + if (!dir_ni) { + res = -errno; + goto exit; + } +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* make sure parent directory is writeable and executable */ + if (!ntfs_fuse_fill_security_context(req, &security) + || ntfs_allowed_create(&security, + dir_ni, &gid, &dsetgid)) { +#else + ntfs_fuse_fill_security_context(req, &security); + ntfs_allowed_create(&security, dir_ni, &gid, &dsetgid); +#endif + if (S_ISDIR(type)) + perm = (typemode & ~ctx->dmask & 0777) + | (dsetgid & S_ISGID); + else + perm = typemode & ~ctx->fmask & 0777; + /* + * Try to get a security id available for + * file creation (from inheritance or argument). + * This is not possible for NTFS 1.x, and we will + * have to build a security attribute later. + */ + if (!ctx->security.mapping[MAPUSERS]) + securid = const_cpu_to_le32(0); + else + if (ctx->inherit) + securid = ntfs_inherited_id(&security, + dir_ni, S_ISDIR(type)); + else +#if POSIXACLS + securid = ntfs_alloc_securid(&security, + security.uid, gid, + dir_ni, perm, S_ISDIR(type)); +#else + securid = ntfs_alloc_securid(&security, + security.uid, gid, + perm & ~security.umask, S_ISDIR(type)); +#endif + /* Create object specified in @type. */ + switch (type) { + case S_IFCHR: + case S_IFBLK: + ni = ntfs_create_device(dir_ni, securid, + uname, uname_len, type, dev); + break; + case S_IFLNK: + utarget_len = ntfs_mbstoucs(target, &utarget); + if (utarget_len < 0) { + res = -errno; + goto exit; + } + ni = ntfs_create_symlink(dir_ni, securid, + uname, uname_len, + utarget, utarget_len); + break; + default: + ni = ntfs_create(dir_ni, securid, uname, + uname_len, type); + break; + } + if (ni) { + /* + * set the security attribute if a security id + * could not be allocated (eg NTFS 1.x) + */ + if (ctx->security.mapping[MAPUSERS]) { +#if POSIXACLS + if (!securid + && ntfs_set_inherited_posix(&security, ni, + security.uid, gid, + dir_ni, perm) < 0) + set_fuse_error(&res); +#else + if (!securid + && ntfs_set_owner_mode(&security, ni, + security.uid, gid, + perm & ~security.umask) < 0) + set_fuse_error(&res); +#endif + } + set_archive(ni); + /* mark a need to compress the end of file */ + if (fi && (ni->flags & FILE_ATTR_COMPRESSED)) { + state |= CLOSE_COMPRESSED; + } +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + /* mark a future need to fixup encrypted inode */ + if (fi + && ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED)) + state |= CLOSE_ENCRYPTED; +#endif /* HAVE_SETXATTR */ + if (fi && ctx->dmtime) + state |= CLOSE_DMTIME; + ntfs_inode_update_mbsname(dir_ni, name, ni->mft_no); + NInoSetDirty(ni); + e->ino = ni->mft_no; + e->generation = 1; + e->attr_timeout = ATTR_TIMEOUT; + e->entry_timeout = ENTRY_TIMEOUT; + res = ntfs_fuse_getstat(&security, ni, &e->attr); + /* + * closing ni requires access to dir_ni to + * synchronize the index, avoid double opening. + */ + if (ntfs_inode_close_in_dir(ni, dir_ni)) + set_fuse_error(&res); + ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME); + } else + res = -errno; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + } else + res = -errno; +#endif + +exit: + free(uname); + if (ntfs_inode_close(dir_ni)) + set_fuse_error(&res); + if (utarget) + free(utarget); + if ((res >= 0) && fi) { + of = (struct open_file*)malloc(sizeof(struct open_file)); + if (of) { + of->parent = 0; + of->ino = e->ino; + of->state = state; + of->next = ctx->open_files; + of->previous = (struct open_file*)NULL; + if (ctx->open_files) + ctx->open_files->previous = of; + ctx->open_files = of; + fi->fh = (long)of; + } + } + return res; +} + +static void ntfs_fuse_create_file(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode, + struct fuse_file_info *fi) +{ + int res; + struct fuse_entry_param entry; + + res = ntfs_fuse_create(req, parent, name, mode & (S_IFMT | 07777), + 0, &entry, NULL, fi); + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_create(req, &entry, fi); +} + +static void ntfs_fuse_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, dev_t rdev) +{ + int res; + struct fuse_entry_param e; + + res = ntfs_fuse_create(req, parent, name, mode & (S_IFMT | 07777), + rdev, &e,NULL,(struct fuse_file_info*)NULL); + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_entry(req, &e); +} + +static void ntfs_fuse_symlink(fuse_req_t req, const char *target, + fuse_ino_t parent, const char *name) +{ + int res; + struct fuse_entry_param entry; + + res = ntfs_fuse_create(req, parent, name, S_IFLNK, 0, + &entry, target, (struct fuse_file_info*)NULL); + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_entry(req, &entry); +} + + +static int ntfs_fuse_newlink(fuse_req_t req __attribute__((unused)), + fuse_ino_t ino, fuse_ino_t newparent, + const char *newname, struct fuse_entry_param *e) +{ + ntfschar *uname = NULL; + ntfs_inode *dir_ni = NULL, *ni; + int res = 0, uname_len; + struct SECURITY_CONTEXT security; + + /* Open file for which create hard link. */ + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + res = -errno; + goto exit; + } + + /* Do not accept linking to a directory (except for renaming) */ + if (e && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + errno = EPERM; + res = -errno; + goto exit; + } + /* Generate unicode filename. */ + uname_len = ntfs_mbstoucs(newname, &uname); + if ((uname_len < 0) + || (ctx->windows_names + && ntfs_forbidden_names(ctx->vol,uname,uname_len))) { + res = -errno; + goto exit; + } + /* Open parent directory. */ + dir_ni = ntfs_inode_open(ctx->vol, INODE(newparent)); + if (!dir_ni) { + res = -errno; + goto exit; + } + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* make sure the target parent directory is writeable */ + if (ntfs_fuse_fill_security_context(req, &security) + && !ntfs_allowed_access(&security,dir_ni,S_IWRITE + S_IEXEC)) + res = -EACCES; + else +#else + ntfs_fuse_fill_security_context(req, &security); +#endif + { + if (ntfs_link(ni, dir_ni, uname, uname_len)) { + res = -errno; + goto exit; + } + ntfs_inode_update_mbsname(dir_ni, newname, ni->mft_no); + if (e) { + e->ino = ni->mft_no; + e->generation = 1; + e->attr_timeout = ATTR_TIMEOUT; + e->entry_timeout = ENTRY_TIMEOUT; + res = ntfs_fuse_getstat(&security, ni, &e->attr); + } + set_archive(ni); + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME); + } +exit: + /* + * Must close dir_ni first otherwise ntfs_inode_sync_file_name(ni) + * may fail because ni may not be in parent's index on the disk yet. + */ + if (ntfs_inode_close(dir_ni)) + set_fuse_error(&res); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + free(uname); + return (res); +} + +static void ntfs_fuse_link(fuse_req_t req, fuse_ino_t ino, + fuse_ino_t newparent, const char *newname) +{ + struct fuse_entry_param entry; + int res; + + res = ntfs_fuse_newlink(req, ino, newparent, newname, &entry); + if (res) + fuse_reply_err(req, -res); + else + fuse_reply_entry(req, &entry); +} + +static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name, + enum RM_TYPES rm_type __attribute__((unused))) +{ + ntfschar *uname = NULL; + ntfschar *ugname; + ntfs_inode *dir_ni = NULL, *ni = NULL; + int res = 0, uname_len; + int ugname_len; + u64 iref; + fuse_ino_t ino; + struct open_file *of; + char ghostname[GHOSTLTH]; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + struct SECURITY_CONTEXT security; +#endif + + /* Open parent directory. */ + dir_ni = ntfs_inode_open(ctx->vol, INODE(parent)); + if (!dir_ni) { + res = -errno; + goto exit; + } + /* Generate unicode filename. */ + uname_len = ntfs_mbstoucs(name, &uname); + if (uname_len < 0) { + res = -errno; + goto exit; + } + /* Open object for delete. */ + iref = ntfs_inode_lookup_by_mbsname(dir_ni, name); + if (iref == (u64)-1) { + res = -errno; + goto exit; + } + ino = (fuse_ino_t)MREF(iref); + /* deny unlinking metadata files */ + if (ino < FILE_first_user) { + res = -EPERM; + goto exit; + } + + ni = ntfs_inode_open(ctx->vol, ino); + if (!ni) { + res = -errno; + goto exit; + } + +#if defined(__sun) && defined (__SVR4) + /* on Solaris : deny unlinking directories */ + if (rm_type + == (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? RM_LINK : RM_DIR)) { + errno = EPERM; + res = -errno; + goto exit; + } +#endif /* defined(__sun) && defined (__SVR4) */ + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* JPA deny unlinking if directory is not writable and executable */ + if (ntfs_fuse_fill_security_context(req, &security) + && !ntfs_allowed_dir_access(&security, dir_ni, ino, ni, + S_IEXEC + S_IWRITE + S_ISVTX)) { + errno = EACCES; + res = -errno; + goto exit; + } +#endif + /* + * We keep one open_file record per opening, to avoid + * having to check the list of open files when opening + * and closing (which are more frequent than unlinking). + * As a consequence, we may have to create several + * ghosts names for the same file. + * The file may have been opened with a different name + * in a different parent directory. The ghost is + * nevertheless created in the parent directory of the + * name being unlinked, and permissions to do so are the + * same as required for unlinking. + */ + for (of=ctx->open_files; of; of = of->next) { + if ((of->ino == ino) && !(of->state & CLOSE_GHOST)) { + /* file was open, create a ghost in unlink parent */ + ntfs_inode *gni; + u64 gref; + + /* ni has to be closed for linking ghost */ + if (ni) { + if (ntfs_inode_close(ni)) { + res = -errno; + goto exit; + } + ni = (ntfs_inode*)NULL; + } + of->state |= CLOSE_GHOST; + of->parent = parent; + of->ghost = ++ctx->latest_ghost; + sprintf(ghostname,ghostformat,of->ghost); + /* Generate unicode filename. */ + ugname = (ntfschar*)NULL; + ugname_len = ntfs_mbstoucs(ghostname, &ugname); + if (ugname_len < 0) { + res = -errno; + goto exit; + } + /* sweep existing ghost if any, ignoring errors */ + gref = ntfs_inode_lookup_by_mbsname(dir_ni, ghostname); + if (gref != (u64)-1) { + gni = ntfs_inode_open(ctx->vol, MREF(gref)); + ntfs_delete(ctx->vol, (char*)NULL, gni, dir_ni, + ugname, ugname_len); + /* ntfs_delete() always closes gni and dir_ni */ + dir_ni = (ntfs_inode*)NULL; + } else { + if (ntfs_inode_close(dir_ni)) { + res = -errno; + goto out; + } + dir_ni = (ntfs_inode*)NULL; + } + free(ugname); + res = ntfs_fuse_newlink(req, of->ino, parent, ghostname, + (struct fuse_entry_param*)NULL); + if (res) + goto out; + /* now reopen then parent directory */ + dir_ni = ntfs_inode_open(ctx->vol, INODE(parent)); + if (!dir_ni) { + res = -errno; + goto exit; + } + } + } + if (!ni) { + ni = ntfs_inode_open(ctx->vol, ino); + if (!ni) { + res = -errno; + goto exit; + } + } + if (ntfs_delete(ctx->vol, (char*)NULL, ni, dir_ni, + uname, uname_len)) + res = -errno; + /* ntfs_delete() always closes ni and dir_ni */ + ni = dir_ni = NULL; +exit: + if (ntfs_inode_close(ni) && !res) + res = -errno; + if (ntfs_inode_close(dir_ni) && !res) + res = -errno; +out : + free(uname); + return res; +} + +static void ntfs_fuse_unlink(fuse_req_t req, fuse_ino_t parent, + const char *name) +{ + int res; + + res = ntfs_fuse_rm(req, parent, name, RM_LINK); + if (res) + fuse_reply_err(req, -res); + else + fuse_reply_err(req, 0); +} + +static int ntfs_fuse_safe_rename(fuse_req_t req, fuse_ino_t ino, + fuse_ino_t parent, const char *name, fuse_ino_t xino, + fuse_ino_t newparent, const char *newname, + const char *tmp) +{ + int ret; + + ntfs_log_trace("Entering\n"); + + ret = ntfs_fuse_newlink(req, xino, newparent, tmp, + (struct fuse_entry_param*)NULL); + if (ret) + return ret; + + ret = ntfs_fuse_rm(req, newparent, newname, RM_ANY); + if (!ret) { + + ret = ntfs_fuse_newlink(req, ino, newparent, newname, + (struct fuse_entry_param*)NULL); + if (ret) + goto restore; + + ret = ntfs_fuse_rm(req, parent, name, RM_ANY); + if (ret) { + if (ntfs_fuse_rm(req, newparent, newname, RM_ANY)) + goto err; + goto restore; + } + } + + goto cleanup; +restore: + if (ntfs_fuse_newlink(req, xino, newparent, newname, + (struct fuse_entry_param*)NULL)) { +err: + ntfs_log_perror("Rename failed. Existing file '%s' was renamed " + "to '%s'", newname, tmp); + } else { +cleanup: + /* + * Condition for this unlink has already been checked in + * "ntfs_fuse_rename_existing_dest()", so it should never + * fail (unless concurrent access to directories when fuse + * is multithreaded) + */ + if (ntfs_fuse_rm(req, newparent, tmp, RM_ANY) < 0) + ntfs_log_perror("Rename failed. Existing file '%s' still present " + "as '%s'", newname, tmp); + } + return ret; +} + +static int ntfs_fuse_rename_existing_dest(fuse_req_t req, fuse_ino_t ino, + fuse_ino_t parent, const char *name, + fuse_ino_t xino, fuse_ino_t newparent, + const char *newname) +{ + int ret, len; + char *tmp; + const char *ext = ".ntfs-3g-"; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + ntfs_inode *newdir_ni; + struct SECURITY_CONTEXT security; +#endif + + ntfs_log_trace("Entering\n"); + + len = strlen(newname) + strlen(ext) + 10 + 1; /* wc(str(2^32)) + \0 */ + tmp = (char*)ntfs_malloc(len); + if (!tmp) + return -errno; + + ret = snprintf(tmp, len, "%s%s%010d", newname, ext, ++ntfs_sequence); + if (ret != len - 1) { + ntfs_log_error("snprintf failed: %d != %d\n", ret, len - 1); + ret = -EOVERFLOW; + } else { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* + * Make sure existing dest can be removed. + * This is only needed if parent directory is + * sticky, because in this situation condition + * for unlinking is different from condition for + * linking + */ + newdir_ni = ntfs_inode_open(ctx->vol, INODE(newparent)); + if (newdir_ni) { + if (!ntfs_fuse_fill_security_context(req,&security) + || ntfs_allowed_dir_access(&security, newdir_ni, + xino, (ntfs_inode*)NULL, + S_IEXEC + S_IWRITE + S_ISVTX)) { + if (ntfs_inode_close(newdir_ni)) + ret = -errno; + else + ret = ntfs_fuse_safe_rename(req, ino, + parent, name, xino, + newparent, newname, + tmp); + } else { + ntfs_inode_close(newdir_ni); + ret = -EACCES; + } + } else + ret = -errno; +#else + ret = ntfs_fuse_safe_rename(req, ino, parent, name, + xino, newparent, newname, tmp); +#endif + } + free(tmp); + return ret; +} + +static void ntfs_fuse_rename(fuse_req_t req, fuse_ino_t parent, + const char *name, fuse_ino_t newparent, + const char *newname) +{ + int ret; + fuse_ino_t ino; + fuse_ino_t xino; + ntfs_inode *ni; + + ntfs_log_debug("rename: old: '%s' new: '%s'\n", name, newname); + + /* + * FIXME: Rename should be atomic. + */ + + ino = ntfs_fuse_inode_lookup(parent, name); + if (ino == (fuse_ino_t)-1) { + ret = -errno; + goto out; + } + /* Check whether target is present */ + xino = ntfs_fuse_inode_lookup(newparent, newname); + if (xino != (fuse_ino_t)-1) { + /* + * Target exists : no need to check whether it + * designates the same inode, this has already + * been checked (by fuse ?) + */ + ni = ntfs_inode_open(ctx->vol, INODE(xino)); + if (!ni) + ret = -errno; + else { + ret = ntfs_check_empty_dir(ni); + if (ret < 0) { + ret = -errno; + ntfs_inode_close(ni); + goto out; + } + + if (ntfs_inode_close(ni)) { + set_fuse_error(&ret); + goto out; + } + ret = ntfs_fuse_rename_existing_dest(req, ino, parent, + name, xino, newparent, newname); + } + } else { + /* target does not exist */ + ret = ntfs_fuse_newlink(req, ino, newparent, newname, + (struct fuse_entry_param*)NULL); + if (ret) + goto out; + + ret = ntfs_fuse_rm(req, parent, name, RM_ANY); + if (ret) + ntfs_fuse_rm(req, newparent, newname, RM_ANY); + } +out: + if (ret) + fuse_reply_err(req, -ret); + else + fuse_reply_err(req, 0); +} + +static void ntfs_fuse_release(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + ntfs_inode *ni = NULL; + ntfs_attr *na = NULL; + struct open_file *of; + char ghostname[GHOSTLTH]; + int res; + + of = (struct open_file*)(long)fi->fh; + /* Only for marked descriptors there is something to do */ + if (!of + || !(of->state & (CLOSE_COMPRESSED + | CLOSE_ENCRYPTED | CLOSE_DMTIME))) { + res = 0; + goto out; + } + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + res = -errno; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + res = -errno; + goto exit; + } + res = 0; + if (of->state & CLOSE_DMTIME) + ntfs_inode_update_times(ni,NTFS_UPDATE_MCTIME); + if (of->state & CLOSE_COMPRESSED) + res = ntfs_attr_pclose(na); +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + if (of->state & CLOSE_ENCRYPTED) + res = ntfs_efs_fixup_attribute(NULL, na); +#endif /* HAVE_SETXATTR */ +exit: + if (na) + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); +out: + /* remove the associate ghost file (even if release failed) */ + if (of) { + if (of->state & CLOSE_GHOST) { + sprintf(ghostname,ghostformat,of->ghost); + ntfs_fuse_rm(req, of->parent, ghostname, RM_ANY); + } + /* remove from open files list */ + if (of->next) + of->next->previous = of->previous; + if (of->previous) + of->previous->next = of->next; + else + ctx->open_files = of->next; + free(of); + } + if (res) + fuse_reply_err(req, -res); + else + fuse_reply_err(req, 0); +} + +static void ntfs_fuse_mkdir(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode) +{ + int res; + struct fuse_entry_param entry; + + res = ntfs_fuse_create(req, parent, name, S_IFDIR | (mode & 07777), + 0, &entry, (char*)NULL, (struct fuse_file_info*)NULL); + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_entry(req, &entry); +} + +static void ntfs_fuse_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + int res; + + res = ntfs_fuse_rm(req, parent, name, RM_DIR); + if (res) + fuse_reply_err(req, -res); + else + fuse_reply_err(req, 0); +} + +static void ntfs_fuse_fsync(fuse_req_t req, + fuse_ino_t ino __attribute__((unused)), + int type __attribute__((unused)), + struct fuse_file_info *fi __attribute__((unused))) +{ + /* sync the full device */ + if (ntfs_device_sync(ctx->vol->dev)) + fuse_reply_err(req, errno); + else + fuse_reply_err(req, 0); +} + +#if defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28) +static void ntfs_fuse_ioctl(fuse_req_t req __attribute__((unused)), + fuse_ino_t ino __attribute__((unused)), + int cmd, void *arg, + struct fuse_file_info *fi __attribute__((unused)), + unsigned flags, const void *data, + size_t in_bufsz, size_t out_bufsz) +{ + ntfs_inode *ni; + char *buf = (char*)NULL; + int bufsz; + int ret = 0; + + if (flags & FUSE_IOCTL_COMPAT) { + ret = -ENOSYS; + } else { + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + ret = -errno; + goto fail; + } + bufsz = (in_bufsz > out_bufsz ? in_bufsz : out_bufsz); + if (bufsz) { + buf = ntfs_malloc(bufsz); + if (!buf) { + ret = ENOMEM; + goto fail; + } + memcpy(buf, data, in_bufsz); + } + ret = ntfs_ioctl(ni, cmd, arg, flags, buf); + if (ntfs_inode_close (ni)) + set_fuse_error(&ret); + } + if (ret) +fail : + fuse_reply_err(req, -ret); + else + fuse_reply_ioctl(req, 0, buf, out_bufsz); + if (buf) + free(buf); +} +#endif /* defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28) */ + +static void ntfs_fuse_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, + uint64_t vidx) +{ + ntfs_inode *ni; + ntfs_attr *na; + LCN lcn; + uint64_t lidx = 0; + int ret = 0; + int cl_per_bl = ctx->vol->cluster_size / blocksize; + + if (blocksize > ctx->vol->cluster_size) { + ret = -EINVAL; + goto done; + } + + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + ret = -errno; + goto done; + } + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ret = -errno; + goto close_inode; + } + + if ((na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED)) + || !NAttrNonResident(na)) { + ret = -EINVAL; + goto close_attr; + } + + if (ntfs_attr_map_whole_runlist(na)) { + ret = -errno; + goto close_attr; + } + + lcn = ntfs_rl_vcn_to_lcn(na->rl, vidx / cl_per_bl); + lidx = (lcn > 0) ? lcn * cl_per_bl + vidx % cl_per_bl : 0; + +close_attr: + ntfs_attr_close(na); +close_inode: + if (ntfs_inode_close(ni)) + set_fuse_error(&ret); +done : + if (ret < 0) + fuse_reply_err(req, -ret); + else + fuse_reply_bmap(req, lidx); +} + +#ifdef HAVE_SETXATTR + +/* + * Name space identifications and prefixes + */ + +enum { + XATTRNS_NONE, + XATTRNS_USER, + XATTRNS_SYSTEM, + XATTRNS_SECURITY, + XATTRNS_TRUSTED, + XATTRNS_OPEN +} ; + +/* + * Check whether access to internal data as an extended + * attribute in system name space is allowed + * + * Returns pointer to inode if allowed, + * NULL and errno set if not allowed + */ + +static ntfs_inode *ntfs_check_access_xattr(fuse_req_t req, + struct SECURITY_CONTEXT *security, + fuse_ino_t ino, int attr, BOOL setting) +{ + ntfs_inode *dir_ni; + ntfs_inode *ni; + BOOL foracl; + BOOL bad; + mode_t acctype; + + ni = (ntfs_inode*)NULL; + foracl = (attr == XATTR_POSIX_ACC) + || (attr == XATTR_POSIX_DEF); + /* + * When accessing Posix ACL, return unsupported if ACL + * were disabled or no user mapping has been defined, + * or trying to change a Windows-inherited ACL. + * However no error will be returned to getfacl + */ + if (((!ntfs_fuse_fill_security_context(req, security) + || (ctx->secure_flags + & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_RAW)))) + || !(ctx->secure_flags & (1 << SECURITY_ACL)) + || (setting && ctx->inherit)) + && foracl) { + if (ctx->silent) + errno = 0; + else + errno = EOPNOTSUPP; + } else { + /* + * parent directory must be executable, and + * for setting a DOS name it must be writeable + */ + if (setting && (attr == XATTR_NTFS_DOS_NAME)) + acctype = S_IEXEC | S_IWRITE; + else + acctype = S_IEXEC; + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + /* basic access was checked previously in a lookup */ + if (ni && (acctype != S_IEXEC)) { + bad = FALSE; + /* do not reopen root */ + if (ni->mft_no == FILE_root) { + /* forbid getting/setting names on root */ + if ((attr == XATTR_NTFS_DOS_NAME) + || !ntfs_real_allowed_access(security, + ni, acctype)) + bad = TRUE; + } else { + dir_ni = ntfs_dir_parent_inode(ni); + if (dir_ni) { + if (!ntfs_real_allowed_access(security, + dir_ni, acctype)) + bad = TRUE; + if (ntfs_inode_close(dir_ni)) + bad = TRUE; + } else + bad = TRUE; + } + if (bad) { + ntfs_inode_close(ni); + ni = (ntfs_inode*)NULL; + } + } + } + return (ni); +} + +/* + * Determine the name space of an extended attribute + */ + +static int xattr_namespace(const char *name) +{ + int namespace; + + if (ctx->streams == NF_STREAMS_INTERFACE_XATTR) { + namespace = XATTRNS_NONE; + if (!strncmp(name, nf_ns_user_prefix, + nf_ns_user_prefix_len) + && (strlen(name) != (size_t)nf_ns_user_prefix_len)) + namespace = XATTRNS_USER; + else if (!strncmp(name, nf_ns_system_prefix, + nf_ns_system_prefix_len) + && (strlen(name) != (size_t)nf_ns_system_prefix_len)) + namespace = XATTRNS_SYSTEM; + else if (!strncmp(name, nf_ns_security_prefix, + nf_ns_security_prefix_len) + && (strlen(name) != (size_t)nf_ns_security_prefix_len)) + namespace = XATTRNS_SECURITY; + else if (!strncmp(name, nf_ns_trusted_prefix, + nf_ns_trusted_prefix_len) + && (strlen(name) != (size_t)nf_ns_trusted_prefix_len)) + namespace = XATTRNS_TRUSTED; + } else + namespace = XATTRNS_OPEN; + return (namespace); +} + +/* + * Fix the prefix of an extended attribute + */ + +static int fix_xattr_prefix(const char *name, int namespace, ntfschar **lename) +{ + int len; + char *prefixed; + + *lename = (ntfschar*)NULL; + switch (namespace) { + case XATTRNS_USER : + /* + * user name space : remove user prefix + */ + len = ntfs_mbstoucs(name + nf_ns_user_prefix_len, lename); + break; + case XATTRNS_SYSTEM : + case XATTRNS_SECURITY : + case XATTRNS_TRUSTED : + /* + * security, trusted and unmapped system name spaces : + * insert ntfs-3g prefix + */ + prefixed = (char*)ntfs_malloc(strlen(xattr_ntfs_3g) + + strlen(name) + 1); + if (prefixed) { + strcpy(prefixed,xattr_ntfs_3g); + strcat(prefixed,name); + len = ntfs_mbstoucs(prefixed, lename); + free(prefixed); + } else + len = -1; + break; + case XATTRNS_OPEN : + /* + * in open name space mode : do no fix prefix + */ + len = ntfs_mbstoucs(name, lename); + break; + default : + len = -1; + } + return (len); +} + +static void ntfs_fuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) +{ + ntfs_attr_search_ctx *actx = NULL; + ntfs_inode *ni; + char *list = (char*)NULL; + int ret = 0; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + struct SECURITY_CONTEXT security; +#endif + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + ntfs_fuse_fill_security_context(req, &security); +#endif + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + ret = -errno; + goto out; + } + /* Return with no result for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) + goto exit; + /* otherwise file must be readable */ +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + if (!ntfs_allowed_access(&security,ni,S_IREAD)) { + ret = -EACCES; + goto exit; + } +#endif + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (!actx) { + ret = -errno; + goto exit; + } + if (size) { + list = (char*)malloc(size); + if (!list) { + ret = -errno; + goto exit; + } + } + + if ((ctx->streams == NF_STREAMS_INTERFACE_XATTR) + || (ctx->streams == NF_STREAMS_INTERFACE_OPENXATTR)) { + ret = ntfs_fuse_listxattr_common(ni, actx, list, size, + ctx->streams == NF_STREAMS_INTERFACE_XATTR); + if (ret < 0) + goto exit; + } + if (errno != ENOENT) + ret = -errno; +exit: + if (actx) + ntfs_attr_put_search_ctx(actx); + if (ntfs_inode_close(ni)) + set_fuse_error(&ret); +out : + if (ret < 0) + fuse_reply_err(req, -ret); + else + if (size) + fuse_reply_buf(req, list, ret); + else + fuse_reply_xattr(req, ret); + free(list); +} + +static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size) +{ + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_attr *na = NULL; + char *value = (char*)NULL; + ntfschar *lename = (ntfschar*)NULL; + int lename_len; + int res; + s64 rsize; + enum SYSTEMXATTRS attr; + int namespace; + struct SECURITY_CONTEXT security; + + attr = ntfs_xattr_system_type(name,ctx->vol); + if (attr != XATTR_UNMAPPED) { + /* + * hijack internal data and ACL retrieval, whatever + * mode was selected for xattr (from the user's + * point of view, ACLs are not xattr) + */ + if (size) + value = (char*)ntfs_malloc(size); +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + if (!size || value) { + ni = ntfs_check_access_xattr(req, &security, ino, + attr, FALSE); + if (ni) { + if (ntfs_allowed_access(&security,ni,S_IREAD)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_getxattr(&security, + attr, ni, dir_ni, value, size); + if (dir_ni && ntfs_inode_close(dir_ni)) + set_fuse_error(&res); + } else + res = -errno; + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } else + res = -errno; +#else + /* + * Standard access control has been done by fuse/kernel + */ + if (!size || value) { + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (ni) { + /* user mapping not mandatory */ + ntfs_fuse_fill_security_context(req, &security); + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_getxattr(&security, + attr, ni, dir_ni, value, size); + if (dir_ni && ntfs_inode_close(dir_ni)) + set_fuse_error(&res); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } else + res = -errno; +#endif + } else + res = -errno; + if (res < 0) + fuse_reply_err(req, -res); + else + if (size) + fuse_reply_buf(req, value, res); + else + fuse_reply_xattr(req, res); + free(value); + return; + } + if (ctx->streams == NF_STREAMS_INTERFACE_NONE) { + res = -EOPNOTSUPP; + goto out; + } + namespace = xattr_namespace(name); + if (namespace == XATTRNS_NONE) { + res = -EOPNOTSUPP; + goto out; + } +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + ntfs_fuse_fill_security_context(req,&security); + /* trusted only readable by root */ + if ((namespace == XATTRNS_TRUSTED) + && security.uid) { + res = -ENODATA; + goto out; + } +#endif + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + res = -errno; + goto out; + } + /* Return with no result for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + res = -ENODATA; + goto exit; + } + /* otherwise file must be readable */ +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + if (!ntfs_allowed_access(&security, ni, S_IREAD)) { + res = -errno; + goto exit; + } +#endif + lename_len = fix_xattr_prefix(name, namespace, &lename); + if (lename_len == -1) { + res = -errno; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, lename, lename_len); + if (!na) { + res = -ENODATA; + goto exit; + } + rsize = na->data_size; + if (ctx->efs_raw + && rsize + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) + rsize = ((na->data_size + 511) & ~511) + 2; + if (size) { + if (size >= (size_t)rsize) { + value = (char*)ntfs_malloc(rsize); + if (value) + res = ntfs_attr_pread(na, 0, rsize, value); + if (!value || (res != rsize)) + res = -errno; + } else + res = -ERANGE; + } else + res = rsize; +exit: + if (na) + ntfs_attr_close(na); + free(lename); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + +out : + if (res < 0) + fuse_reply_err(req, -res); + else + if (size) + fuse_reply_buf(req, value, res); + else + fuse_reply_xattr(req, res); + free(value); +} + +static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags) +{ + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_attr *na = NULL; + ntfschar *lename = NULL; + int res, lename_len; + size_t total; + s64 part; + enum SYSTEMXATTRS attr; + int namespace; + struct SECURITY_CONTEXT security; + + attr = ntfs_xattr_system_type(name,ctx->vol); + if (attr != XATTR_UNMAPPED) { + /* + * hijack internal data and ACL setting, whatever + * mode was selected for xattr (from the user's + * point of view, ACLs are not xattr) + * Note : ctime updated on successful settings + */ +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + ni = ntfs_check_access_xattr(req,&security,ino,attr,TRUE); + if (ni) { + if (ntfs_allowed_as_owner(&security, ni)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_setxattr(&security, + attr, ni, dir_ni, value, size, flags); + /* never have to close dir_ni */ + if (res) + res = -errno; + } else + res = -errno; + if (attr != XATTR_NTFS_DOS_NAME) { + if (!res) + ntfs_fuse_update_times(ni, + NTFS_UPDATE_CTIME); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } else + res = -errno; +#else + /* creation of a new name is not controlled by fuse */ + if (attr == XATTR_NTFS_DOS_NAME) + ni = ntfs_check_access_xattr(req, &security, + ino, attr, TRUE); + else + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (ni) { + /* + * user mapping is not mandatory + * if defined, only owner is allowed + */ + if (!ntfs_fuse_fill_security_context(req, &security) + || ntfs_allowed_as_owner(&security, ni)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_setxattr(&security, + attr, ni, dir_ni, value, size, flags); + /* never have to close dir_ni */ + if (res) + res = -errno; + } else + res = -errno; + if (attr != XATTR_NTFS_DOS_NAME) { + if (!res) + ntfs_fuse_update_times(ni, + NTFS_UPDATE_CTIME); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } else + res = -errno; +#endif +#if CACHEING && !defined(FUSE_INTERNAL) + /* + * Most of system xattr settings cause changes to some + * file attribute (st_mode, st_nlink, st_mtime, etc.), + * so we must invalidate cached data when cacheing is + * in use (not possible with internal fuse or external + * fuse before 2.8) + */ + if ((res >= 0) + && fuse_lowlevel_notify_inval_inode(ctx->fc, ino, -1, 0)) + res = -errno; +#endif + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_err(req, 0); + return; + } + if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR) + && (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR)) { + res = -EOPNOTSUPP; + goto out; + } + namespace = xattr_namespace(name); + if (namespace == XATTRNS_NONE) { + res = -EOPNOTSUPP; + goto out; + } +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + ntfs_fuse_fill_security_context(req,&security); + /* security and trusted only settable by root */ + if (((namespace == XATTRNS_SECURITY) + || (namespace == XATTRNS_TRUSTED)) + && security.uid) { + res = -EPERM; + goto out; + } +#endif + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + res = -errno; + goto out; + } +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + switch (namespace) { + case XATTRNS_SECURITY : + case XATTRNS_TRUSTED : + if (security.uid) { + res = -EPERM; + goto exit; + } + break; + case XATTRNS_SYSTEM : + if (!ntfs_allowed_as_owner(&security, ni)) { + res = -EACCES; + goto exit; + } + break; + default : + /* User xattr not allowed for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + res = -EPERM; + goto exit; + } + if (!ntfs_allowed_access(&security,ni,S_IWRITE)) { + res = -EACCES; + goto exit; + } + break; + } +#else + /* User xattr not allowed for symlinks, fifo, etc. */ + if ((namespace == XATTRNS_USER) + && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { + res = -EPERM; + goto exit; + } +#endif + lename_len = fix_xattr_prefix(name, namespace, &lename); + if ((lename_len == -1) + || (ctx->windows_names + && ntfs_forbidden_chars(lename,lename_len))) { + res = -errno; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, lename, lename_len); + if (na && flags == XATTR_CREATE) { + res = -EEXIST; + goto exit; + } + if (!na) { + if (flags == XATTR_REPLACE) { + res = -ENODATA; + goto exit; + } + if (ntfs_attr_add(ni, AT_DATA, lename, lename_len, NULL, 0)) { + res = -errno; + goto exit; + } + if (!(ni->flags & FILE_ATTR_ARCHIVE)) { + set_archive(ni); + NInoFileNameSetDirty(ni); + } + na = ntfs_attr_open(ni, AT_DATA, lename, lename_len); + if (!na) { + res = -errno; + goto exit; + } + } else { + /* currently compressed streams can only be wiped out */ + if (ntfs_attr_truncate(na, (s64)0 /* size */)) { + res = -errno; + goto exit; + } + } + total = 0; + res = 0; + if (size) { + do { + part = ntfs_attr_pwrite(na, total, size - total, + &value[total]); + if (part > 0) + total += part; + } while ((part > 0) && (total < size)); + } + if ((total != size) || ntfs_attr_pclose(na)) + res = -errno; + else { + if (ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED)) { + if (ntfs_efs_fixup_attribute(NULL,na)) + res = -errno; + } + } + if (!res) { + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + if (!(ni->flags & FILE_ATTR_ARCHIVE)) { + set_archive(ni); + NInoFileNameSetDirty(ni); + } + } +exit: + if (na) + ntfs_attr_close(na); + free(lename); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); +out : + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_err(req, 0); +} + +static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) +{ + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfschar *lename = NULL; + int res = 0, lename_len; + enum SYSTEMXATTRS attr; + int namespace; + struct SECURITY_CONTEXT security; + + attr = ntfs_xattr_system_type(name,ctx->vol); + if (attr != XATTR_UNMAPPED) { + switch (attr) { + /* + * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES + * is never allowed + */ + case XATTR_NTFS_ACL : + case XATTR_NTFS_ATTRIB : + case XATTR_NTFS_ATTRIB_BE : + case XATTR_NTFS_EFSINFO : + case XATTR_NTFS_TIMES : + case XATTR_NTFS_TIMES_BE : + case XATTR_NTFS_CRTIME : + case XATTR_NTFS_CRTIME_BE : + res = -EPERM; + break; + default : +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + ni = ntfs_check_access_xattr(req, &security, ino, + attr,TRUE); + if (ni) { + if (ntfs_allowed_as_owner(&security, ni)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_removexattr(&security, + attr, ni, dir_ni); + if (res) + res = -errno; + /* never have to close dir_ni */ + } else + res = -errno; + if (attr != XATTR_NTFS_DOS_NAME) { + if (!res) + ntfs_fuse_update_times(ni, + NTFS_UPDATE_CTIME); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } else + res = -errno; +#else + /* creation of a new name is not controlled by fuse */ + if (attr == XATTR_NTFS_DOS_NAME) + ni = ntfs_check_access_xattr(req, &security, + ino, attr, TRUE); + else + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (ni) { + /* + * user mapping is not mandatory + * if defined, only owner is allowed + */ + if (!ntfs_fuse_fill_security_context(req, &security) + || ntfs_allowed_as_owner(&security, ni)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = ntfs_dir_parent_inode(ni); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_removexattr(&security, + attr, ni, dir_ni); + /* never have to close dir_ni */ + if (res) + res = -errno; + } else + res = -errno; + if (attr != XATTR_NTFS_DOS_NAME) { + if (!res) + ntfs_fuse_update_times(ni, + NTFS_UPDATE_CTIME); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } else + res = -errno; +#endif +#if CACHEING && !defined(FUSE_INTERNAL) + /* + * Some allowed system xattr removals cause changes to + * some file attribute (st_mode, st_nlink, etc.), + * so we must invalidate cached data when cacheing is + * in use (not possible with internal fuse or external + * fuse before 2.8) + */ + if ((res >= 0) + && fuse_lowlevel_notify_inval_inode(ctx->fc, + ino, -1, 0)) + res = -errno; +#endif + break; + } + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_err(req, 0); + return; + } + if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR) + && (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR)) { + res = -EOPNOTSUPP; + goto out; + } + namespace = xattr_namespace(name); + if (namespace == XATTRNS_NONE) { + res = -EOPNOTSUPP; + goto out; + } +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + ntfs_fuse_fill_security_context(req,&security); + /* security and trusted only settable by root */ + if (((namespace == XATTRNS_SECURITY) + || (namespace == XATTRNS_TRUSTED)) + && security.uid) { + res = -EACCES; + goto out; + } +#endif + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (!ni) { + res = -errno; + goto out; + } +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + switch (namespace) { + case XATTRNS_SECURITY : + case XATTRNS_TRUSTED : + if (security.uid) { + res = -EPERM; + goto exit; + } + break; + case XATTRNS_SYSTEM : + if (!ntfs_allowed_as_owner(&security, ni)) { + res = -EACCES; + goto exit; + } + break; + default : + /* User xattr not allowed for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + res = -EPERM; + goto exit; + } + if (!ntfs_allowed_access(&security,ni,S_IWRITE)) { + res = -EACCES; + goto exit; + } + break; + } +#else + /* User xattr not allowed for symlinks, fifo, etc. */ + if ((namespace == XATTRNS_USER) + && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { + res = -EPERM; + goto exit; + } +#endif + lename_len = fix_xattr_prefix(name, namespace, &lename); + if (lename_len == -1) { + res = -errno; + goto exit; + } + if (ntfs_attr_remove(ni, AT_DATA, lename, lename_len)) { + if (errno == ENOENT) + errno = ENODATA; + res = -errno; + } + if (!res) { + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + if (!(ni->flags & FILE_ATTR_ARCHIVE)) { + set_archive(ni); + NInoFileNameSetDirty(ni); + } + } +exit: + free(lename); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); +out : + if (res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_err(req, 0); + return; + +} + +#else +#if POSIXACLS +#error "Option inconsistency : POSIXACLS requires SETXATTR" +#endif +#endif /* HAVE_SETXATTR */ + +static void ntfs_close(void) +{ + struct SECURITY_CONTEXT security; + + if (!ctx) + return; + + if (!ctx->vol) + return; + + if (ctx->mounted) { + ntfs_log_info("Unmounting %s (%s)\n", opts.device, + ctx->vol->vol_name); + if (ntfs_fuse_fill_security_context((fuse_req_t)NULL, &security)) { + if (ctx->seccache && ctx->seccache->head.p_reads) { + ntfs_log_info("Permissions cache : %lu writes, " + "%lu reads, %lu.%1lu%% hits\n", + ctx->seccache->head.p_writes, + ctx->seccache->head.p_reads, + 100 * ctx->seccache->head.p_hits + / ctx->seccache->head.p_reads, + 1000 * ctx->seccache->head.p_hits + / ctx->seccache->head.p_reads % 10); + } + } + ntfs_close_secure(&security); + } + + if (ntfs_umount(ctx->vol, FALSE)) + ntfs_log_perror("Failed to close volume %s", opts.device); + + ctx->vol = NULL; +} + +static void ntfs_fuse_destroy2(void *notused __attribute__((unused))) +{ + ntfs_close(); +} + +static struct fuse_lowlevel_ops ntfs_3g_ops = { + .lookup = ntfs_fuse_lookup, + .getattr = ntfs_fuse_getattr, + .readlink = ntfs_fuse_readlink, + .opendir = ntfs_fuse_opendir, + .readdir = ntfs_fuse_readdir, + .releasedir = ntfs_fuse_releasedir, + .open = ntfs_fuse_open, + .release = ntfs_fuse_release, + .read = ntfs_fuse_read, + .write = ntfs_fuse_write, + .setattr = ntfs_fuse_setattr, + .statfs = ntfs_fuse_statfs, + .create = ntfs_fuse_create_file, + .mknod = ntfs_fuse_mknod, + .symlink = ntfs_fuse_symlink, + .link = ntfs_fuse_link, + .unlink = ntfs_fuse_unlink, + .rename = ntfs_fuse_rename, + .mkdir = ntfs_fuse_mkdir, + .rmdir = ntfs_fuse_rmdir, + .fsync = ntfs_fuse_fsync, + .fsyncdir = ntfs_fuse_fsync, + .bmap = ntfs_fuse_bmap, + .destroy = ntfs_fuse_destroy2, +#if defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28) + .ioctl = ntfs_fuse_ioctl, +#endif /* defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28) */ +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + .access = ntfs_fuse_access, +#endif +#ifdef HAVE_SETXATTR + .getxattr = ntfs_fuse_getxattr, + .setxattr = ntfs_fuse_setxattr, + .removexattr = ntfs_fuse_removexattr, + .listxattr = ntfs_fuse_listxattr, +#endif /* HAVE_SETXATTR */ +#if 0 && (defined(__APPLE__) || defined(__DARWIN__)) /* Unfinished. */ + /* MacFUSE extensions. */ + .getxtimes = ntfs_macfuse_getxtimes, + .setcrtime = ntfs_macfuse_setcrtime, + .setbkuptime = ntfs_macfuse_setbkuptime, + .setchgtime = ntfs_macfuse_setchgtime, +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + .init = ntfs_init +}; + +static int ntfs_fuse_init(void) +{ + ctx = (ntfs_fuse_context_t*)ntfs_calloc(sizeof(ntfs_fuse_context_t)); + if (!ctx) + return -1; + + *ctx = (ntfs_fuse_context_t) { + .uid = getuid(), + .gid = getgid(), +#if defined(linux) + .streams = NF_STREAMS_INTERFACE_XATTR, +#else + .streams = NF_STREAMS_INTERFACE_NONE, +#endif + .atime = ATIME_RELATIVE, + .silent = TRUE, + .recover = TRUE + }; + return 0; +} + +static int ntfs_open(const char *device) +{ + unsigned long flags = 0; + ntfs_volume *vol; + + if (!ctx->blkdev) + flags |= NTFS_MNT_EXCLUSIVE; + if (ctx->ro) + flags |= NTFS_MNT_RDONLY; + if (ctx->recover) + flags |= NTFS_MNT_RECOVER; + if (ctx->hiberfile) + flags |= NTFS_MNT_IGNORE_HIBERFILE; + + ctx->vol = vol = ntfs_mount(device, flags); + if (!vol) { + ntfs_log_perror("Failed to mount '%s'", device); + goto err_out; + } + if (ctx->sync && ctx->vol->dev) + NDevSetSync(ctx->vol->dev); + if (ctx->compression) + NVolSetCompression(ctx->vol); + else + NVolClearCompression(ctx->vol); +#ifdef HAVE_SETXATTR + /* archivers must see hidden files */ + if (ctx->efs_raw) + ctx->hide_hid_files = FALSE; +#endif + if (ntfs_set_shown_files(ctx->vol, ctx->show_sys_files, + !ctx->hide_hid_files, ctx->hide_dot_files)) + goto err_out; + + if (ctx->ignore_case && ntfs_set_ignore_case(vol)) + goto err_out; + + vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na); + if (vol->free_clusters < 0) { + ntfs_log_perror("Failed to read NTFS $Bitmap"); + goto err_out; + } + + vol->free_mft_records = ntfs_get_nr_free_mft_records(vol); + if (vol->free_mft_records < 0) { + ntfs_log_perror("Failed to calculate free MFT records"); + goto err_out; + } + + if (ctx->hiberfile && ntfs_volume_check_hiberfile(vol, 0)) { + if (errno != EPERM) + goto err_out; + if (ntfs_fuse_rm((fuse_req_t)NULL,FILE_root,"hiberfil.sys", + RM_LINK)) + goto err_out; + } + + errno = 0; +err_out: + return ntfs_volume_error(errno); + +} + +static void usage(void) +{ + ntfs_log_info(usage_msg, EXEC_NAME, VERSION, FUSE_TYPE, fuse_version(), + 5 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING, + EXEC_NAME, ntfs_home); +} + +#if defined(linux) || defined(__uClinux__) + +static const char *dev_fuse_msg = +"HINT: You should be root, or make ntfs-3g setuid root, or load the FUSE\n" +" kernel module as root ('modprobe fuse' or 'insmod <path_to>/fuse.ko'" +" or insmod <path_to>/fuse.o'). Make also sure that the fuse device" +" exists. It's usually either /dev/fuse or /dev/misc/fuse."; + +static const char *fuse26_kmod_msg = +"WARNING: Deficient Linux kernel detected. Some driver features are\n" +" not available (swap file on NTFS, boot from NTFS by LILO), and\n" +" unmount is not safe unless it's made sure the ntfs-3g process\n" +" naturally terminates after calling 'umount'. If you wish this\n" +" message to disappear then you should upgrade to at least kernel\n" +" version 2.6.20, or request help from your distribution to fix\n" +" the kernel problem. The below web page has more information:\n" +" http://tuxera.com/community/ntfs-3g-faq/#fuse26\n" +"\n"; + +static void mknod_dev_fuse(const char *dev) +{ + struct stat st; + + if (stat(dev, &st) && (errno == ENOENT)) { + mode_t mask = umask(0); + if (mknod(dev, S_IFCHR | 0666, makedev(10, 229))) { + ntfs_log_perror("Failed to create '%s'", dev); + if (errno == EPERM) + ntfs_log_error("%s", dev_fuse_msg); + } + umask(mask); + } +} + +static void create_dev_fuse(void) +{ + mknod_dev_fuse("/dev/fuse"); + +#ifdef __UCLIBC__ + { + struct stat st; + /* The fuse device is under /dev/misc using devfs. */ + if (stat("/dev/misc", &st) && (errno == ENOENT)) { + mode_t mask = umask(0); + mkdir("/dev/misc", 0775); + umask(mask); + } + mknod_dev_fuse("/dev/misc/fuse"); + } +#endif +} + +static fuse_fstype get_fuse_fstype(void) +{ + char buf[256]; + fuse_fstype fstype = FSTYPE_NONE; + + FILE *f = fopen("/proc/filesystems", "r"); + if (!f) { + ntfs_log_perror("Failed to open /proc/filesystems"); + return FSTYPE_UNKNOWN; + } + + while (fgets(buf, sizeof(buf), f)) { + if (strstr(buf, "fuseblk\n")) { + fstype = FSTYPE_FUSEBLK; + break; + } + if (strstr(buf, "fuse\n")) + fstype = FSTYPE_FUSE; + } + + fclose(f); + return fstype; +} + +static fuse_fstype load_fuse_module(void) +{ + int i; + struct stat st; + pid_t pid; + const char *cmd = "/sbin/modprobe"; + struct timespec req = { 0, 100000000 }; /* 100 msec */ + fuse_fstype fstype; + + if (!stat(cmd, &st) && !geteuid()) { + pid = fork(); + if (!pid) { + execl(cmd, cmd, "fuse", NULL); + _exit(1); + } else if (pid != -1) + waitpid(pid, NULL, 0); + } + + for (i = 0; i < 10; i++) { + /* + * We sleep first because despite the detection of the loaded + * FUSE kernel module, fuse_mount() can still fail if it's not + * fully functional/initialized. Note, of course this is still + * unreliable but usually helps. + */ + nanosleep(&req, NULL); + fstype = get_fuse_fstype(); + if (fstype != FSTYPE_NONE) + break; + } + return fstype; +} + +#endif + +static struct fuse_chan *try_fuse_mount(char *parsed_options) +{ + struct fuse_chan *fc = NULL; + struct fuse_args margs = FUSE_ARGS_INIT(0, NULL); + + /* The fuse_mount() options get modified, so we always rebuild it */ + if ((fuse_opt_add_arg(&margs, EXEC_NAME) == -1 || + fuse_opt_add_arg(&margs, "-o") == -1 || + fuse_opt_add_arg(&margs, parsed_options) == -1)) { + ntfs_log_error("Failed to set FUSE options.\n"); + goto free_args; + } + + fc = fuse_mount(opts.mnt_point, &margs); +free_args: + fuse_opt_free_args(&margs); + return fc; + +} + +static int set_fuseblk_options(char **parsed_options) +{ + char options[64]; + long pagesize; + u32 blksize = ctx->vol->cluster_size; + + pagesize = sysconf(_SC_PAGESIZE); + if (pagesize < 1) + pagesize = 4096; + + if (blksize > (u32)pagesize) + blksize = pagesize; + + snprintf(options, sizeof(options), ",blkdev,blksize=%u", blksize); + if (ntfs_strappend(parsed_options, options)) + return -1; + return 0; +} + +static struct fuse_session *mount_fuse(char *parsed_options) +{ + struct fuse_session *se = NULL; + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + + ctx->fc = try_fuse_mount(parsed_options); + if (!ctx->fc) + return NULL; + + if (fuse_opt_add_arg(&args, "") == -1) + goto err; + if (ctx->debug) + if (fuse_opt_add_arg(&args, "-odebug") == -1) + goto err; + + se = fuse_lowlevel_new(&args , &ntfs_3g_ops, sizeof(ntfs_3g_ops), NULL); + if (!se) + goto err; + + + if (fuse_set_signal_handlers(se)) + goto err_destroy; + fuse_session_add_chan(se, ctx->fc); +out: + fuse_opt_free_args(&args); + return se; +err_destroy: + fuse_session_destroy(se); + se = NULL; +err: + fuse_unmount(opts.mnt_point, ctx->fc); + goto out; +} + +static void setup_logging(char *parsed_options) +{ + if (!ctx->no_detach) { + if (daemon(0, ctx->debug)) + ntfs_log_error("Failed to daemonize.\n"); + else if (!ctx->debug) { +#ifndef DEBUG + ntfs_log_set_handler(ntfs_log_handler_syslog); + /* Override default libntfs identify. */ + openlog(EXEC_NAME, LOG_PID, LOG_DAEMON); +#endif + } + } + + ctx->seccache = (struct PERMISSIONS_CACHE*)NULL; + + ntfs_log_info("Version %s %s %d\n", VERSION, FUSE_TYPE, fuse_version()); + if (strcmp(opts.arg_device,opts.device)) + ntfs_log_info("Requested device %s canonicalized as %s\n", + opts.arg_device,opts.device); + ntfs_log_info("Mounted %s (%s, label \"%s\", NTFS %d.%d)\n", + opts.device, (ctx->ro) ? "Read-Only" : "Read-Write", + ctx->vol->vol_name, ctx->vol->major_ver, + ctx->vol->minor_ver); + ntfs_log_info("Cmdline options: %s\n", opts.options ? opts.options : ""); + ntfs_log_info("Mount options: %s\n", parsed_options); +} + +int main(int argc, char *argv[]) +{ + char *parsed_options = NULL; + struct fuse_session *se; +#if !(defined(__sun) && defined (__SVR4)) + fuse_fstype fstype = FSTYPE_UNKNOWN; +#endif + const char *permissions_mode = (const char*)NULL; + const char *failed_secure = (const char*)NULL; +#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) + struct XATTRMAPPING *xattr_mapping = (struct XATTRMAPPING*)NULL; +#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ + struct stat sbuf; + unsigned long existing_mount; + int err, fd; + + /* + * Make sure file descriptors 0, 1 and 2 are open, + * otherwise chaos would ensue. + */ + do { + fd = open("/dev/null", O_RDWR); + if (fd > 2) + close(fd); + } while (fd >= 0 && fd <= 2); + +#ifndef FUSE_INTERNAL + if ((getuid() != geteuid()) || (getgid() != getegid())) { + fprintf(stderr, "%s", setuid_msg); + return NTFS_VOLUME_INSECURE; + } +#endif + if (drop_privs()) + return NTFS_VOLUME_NO_PRIVILEGE; + + ntfs_set_locale(); + ntfs_log_set_handler(ntfs_log_handler_stderr); + + if (ntfs_parse_options(&opts, usage, argc, argv)) { + usage(); + return NTFS_VOLUME_SYNTAX_ERROR; + } + + if (ntfs_fuse_init()) { + err = NTFS_VOLUME_OUT_OF_MEMORY; + goto err2; + } + + parsed_options = parse_mount_options(ctx, &opts, TRUE); + if (!parsed_options) { + err = NTFS_VOLUME_SYNTAX_ERROR; + goto err_out; + } + if (!ntfs_check_if_mounted(opts.device,&existing_mount) + && (existing_mount & NTFS_MF_MOUNTED) + /* accept multiple read-only mounts */ + && (!(existing_mount & NTFS_MF_READONLY) || !ctx->ro)) { + err = NTFS_VOLUME_LOCKED; + goto err_out; + } + + /* need absolute mount point for junctions */ + if (opts.mnt_point[0] == '/') + ctx->abs_mnt_point = strdup(opts.mnt_point); + else { + ctx->abs_mnt_point = (char*)ntfs_malloc(PATH_MAX); + if (ctx->abs_mnt_point) { + if (getcwd(ctx->abs_mnt_point, + PATH_MAX - strlen(opts.mnt_point) - 1)) { + strcat(ctx->abs_mnt_point, "/"); + strcat(ctx->abs_mnt_point, opts.mnt_point); +#if defined(__sun) && defined (__SVR4) + /* Solaris also wants the absolute mount point */ + opts.mnt_point = ctx->abs_mnt_point; +#endif /* defined(__sun) && defined (__SVR4) */ + } + } + } + if (!ctx->abs_mnt_point) { + err = NTFS_VOLUME_OUT_OF_MEMORY; + goto err_out; + } + + ctx->security.uid = 0; + ctx->security.gid = 0; + if ((opts.mnt_point[0] == '/') + && !stat(opts.mnt_point,&sbuf)) { + /* collect owner of mount point, useful for default mapping */ + ctx->security.uid = sbuf.st_uid; + ctx->security.gid = sbuf.st_gid; + } + +#if defined(linux) || defined(__uClinux__) + fstype = get_fuse_fstype(); + + err = NTFS_VOLUME_NO_PRIVILEGE; + if (restore_privs()) + goto err_out; + + if (fstype == FSTYPE_NONE || fstype == FSTYPE_UNKNOWN) + fstype = load_fuse_module(); + create_dev_fuse(); + + if (drop_privs()) + goto err_out; +#endif + if (stat(opts.device, &sbuf)) { + ntfs_log_perror("Failed to access '%s'", opts.device); + err = NTFS_VOLUME_NO_PRIVILEGE; + goto err_out; + } + +#if !(defined(__sun) && defined (__SVR4)) + /* Always use fuseblk for block devices unless it's surely missing. */ + if (S_ISBLK(sbuf.st_mode) && (fstype != FSTYPE_FUSE)) + ctx->blkdev = TRUE; +#endif + +#ifndef FUSE_INTERNAL + if (getuid() && ctx->blkdev) { + ntfs_log_error("%s", unpriv_fuseblk_msg); + err = NTFS_VOLUME_NO_PRIVILEGE; + goto err2; + } +#endif + err = ntfs_open(opts.device); + if (err) + goto err_out; + + /* Force read-only mount if the device was found read-only */ + if (!ctx->ro && NVolReadOnly(ctx->vol)) { + ctx->ro = TRUE; + if (ntfs_strinsert(&parsed_options, ",ro")) + goto err_out; + } + /* We must do this after ntfs_open() to be able to set the blksize */ + if (ctx->blkdev && set_fuseblk_options(&parsed_options)) + goto err_out; + + ctx->security.vol = ctx->vol; + ctx->vol->secure_flags = ctx->secure_flags; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + ctx->vol->efs_raw = ctx->efs_raw; +#endif /* HAVE_SETXATTR */ + /* JPA open $Secure, (whatever NTFS version !) */ + /* to initialize security data */ + if (ntfs_open_secure(ctx->vol) && (ctx->vol->major_ver >= 3)) + failed_secure = "Could not open file $Secure"; + if (!ntfs_build_mapping(&ctx->security,ctx->usermap_path, + (ctx->vol->secure_flags + & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL))) + && !ctx->inherit + && !(ctx->vol->secure_flags & (1 << SECURITY_WANTED)))) { +#if POSIXACLS + /* use basic permissions if requested */ + if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT)) + permissions_mode = "User mapping built, Posix ACLs not used"; + else { + permissions_mode = "User mapping built, Posix ACLs in use"; +#if KERNELACLS + if (ntfs_strinsert(&parsed_options, + ",default_permissions,acl")) { + err = NTFS_VOLUME_SYNTAX_ERROR; + goto err_out; + } +#endif /* KERNELACLS */ + } +#else /* POSIXACLS */ + if (!(ctx->vol->secure_flags + & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL)))) { + /* + * No explicit option but user mapping found + * force default security + */ +#if KERNELPERMS + ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT); + if (ntfs_strinsert(&parsed_options, ",default_permissions")) { + err = NTFS_VOLUME_SYNTAX_ERROR; + goto err_out; + } +#endif /* KERNELPERMS */ + } + permissions_mode = "User mapping built"; +#endif /* POSIXACLS */ + ctx->dmask = ctx->fmask = 0; + } else { + ctx->security.uid = ctx->uid; + ctx->security.gid = ctx->gid; + /* same ownership/permissions for all files */ + ctx->security.mapping[MAPUSERS] = (struct MAPPING*)NULL; + ctx->security.mapping[MAPGROUPS] = (struct MAPPING*)NULL; + if ((ctx->vol->secure_flags & (1 << SECURITY_WANTED)) + && !(ctx->vol->secure_flags & (1 << SECURITY_DEFAULT))) { + ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT); + if (ntfs_strinsert(&parsed_options, ",default_permissions")) { + err = NTFS_VOLUME_SYNTAX_ERROR; + goto err_out; + } + } + if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT)) { + ctx->vol->secure_flags |= (1 << SECURITY_RAW); + permissions_mode = "Global ownership and permissions enforced"; + } else { + ctx->vol->secure_flags &= ~(1 << SECURITY_RAW); + permissions_mode = "Ownership and permissions disabled"; + } + } + if (ctx->usermap_path) + free (ctx->usermap_path); + +#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) + xattr_mapping = ntfs_xattr_build_mapping(ctx->vol, + ctx->xattrmap_path); + ctx->vol->xattr_mapping = xattr_mapping; + /* + * Errors are logged, do not refuse mounting, it would be + * too difficult to fix the unmountable mapping file. + */ + if (ctx->xattrmap_path) + free(ctx->xattrmap_path); +#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ + + se = mount_fuse(parsed_options); + if (!se) { + err = NTFS_VOLUME_FUSE_ERROR; + goto err_out; + } + + ctx->mounted = TRUE; + +#if defined(linux) || defined(__uClinux__) + if (S_ISBLK(sbuf.st_mode) && (fstype == FSTYPE_FUSE)) + ntfs_log_info("%s", fuse26_kmod_msg); +#endif + setup_logging(parsed_options); + if (failed_secure) + ntfs_log_info("%s\n",failed_secure); + if (permissions_mode) + ntfs_log_info("%s, configuration type %d\n",permissions_mode, + 5 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING); + + fuse_session_loop(se); + fuse_remove_signal_handlers(se); + + err = 0; + + fuse_unmount(opts.mnt_point, ctx->fc); + fuse_session_destroy(se); +err_out: + ntfs_mount_error(opts.device, opts.mnt_point, err); + if (ctx->abs_mnt_point) + free(ctx->abs_mnt_point); +#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) + ntfs_xattr_free_mapping(xattr_mapping); +#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ +err2: + ntfs_close(); + free(ctx); + free(parsed_options); + free(opts.options); + free(opts.device); + return err; +} + diff --git a/src/ntfs-3g.8 b/src/ntfs-3g.8 new file mode 100755 index 0000000000000000000000000000000000000000..65b98be0f4870e5441d01fa010909129cdeb479d --- /dev/null +++ b/src/ntfs-3g.8 @@ -0,0 +1,465 @@ +.\" Copyright (c) 2005-2006 Yura Pakhuchiy. +.\" Copyright (c) 2005 Richard Russon. +.\" Copyright (c) 2006-2009 Szabolcs Szakacsits. +.\" Copyright (c) 2009-2014 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFS-3G 8 "Mar 2014" "ntfs-3g 2015.3.14" +.SH NAME +ntfs-3g \- Third Generation Read/Write NTFS Driver +.SH SYNOPSIS +.B ntfs-3g +\fB[-o \fIoption\fP\fB[,...]]\fR +.I volume mount_point +.br +.B mount \-t ntfs-3g +\fB[-o \fIoption\fP\fB[,...]]\fR +.I volume mount_point +.br +.B lowntfs-3g +\fB[-o \fIoption\fP\fB[,...]]\fR +.I volume mount_point +.br +.B mount \-t lowntfs-3g +\fB[-o \fIoption\fP\fB[,...]]\fR +.I volume mount_point +.SH DESCRIPTION +\fBntfs-3g\fR is an NTFS driver, which can create, remove, rename, move +files, directories, hard links, and streams; it can read and write files, +including streams, sparse files and transparently compressed files; it can +handle special files like symbolic links, devices, and FIFOs; moreover it +provides standard management of file ownership and permissions, including +POSIX ACLs. +.PP +It comes in two variants \fBntfs-3g\fR and \fBlowntfs-3g\fR with +a few differences mentioned below in relevant options descriptions. +.PP +The \fIvolume\fR to be mounted can be either a block device or +an image file. +.SS Windows hibernation and fast restarting +On computers which can be dual-booted into Windows or Linux, Windows has +to be fully shut down before booting into Linux, otherwise the NTFS file +systems on internal disks may be left in an inconsistent state and changes +made by Linux may be ignored by Windows. +.P +So, Windows may not be left in hibernation when starting Linux, in order +to avoid inconsistencies. Moreover, the fast restart feature available on +recent Windows systems has to be disabled. This can be achieved by issuing +as an Administrator the Windows command which disables both +hibernation and fast restarting : +.RS +.sp +powercfg /h off +.sp +.RE +.SS Access Handling and Security +By default, files and directories are owned by the effective +user and group of the mounting process, and everybody has +full read, write, execution and directory browsing permissions. +You can also assign permissions to a single user by using the +.B uid +and/or the +.B gid +options together with the +.B umask, +or +.B fmask +and +.B dmask +options. +.PP +Doing so, Windows users have full access to the files created by +.B ntfs-3g. +.PP +But, by setting the \fBpermissions\fR option, you can benefit from the full +ownership and permissions features as defined by POSIX. Moreover, by defining +a Windows-to-Linux user mapping, the ownerships and permissions are even +applied to Windows users and conversely. +.PP +If +.B ntfs-3g +is set setuid-root then non-root users will +be also able to mount volumes. +.SS Windows Filename Compatibility +NTFS supports several filename namespaces: DOS, Win32 and POSIX. While the +\fBntfs-3g\fR driver handles all of them, it always creates new files in the +POSIX namespace for maximum portability and interoperability reasons. +This means that filenames are case sensitive and all characters are +allowed except '/' and '\\0'. This is perfectly legal on Windows, though +some application may get confused. The option \fBwindows_names\fP may be +used to apply Windows restrictions to new file names. +.SS Alternate Data Streams (ADS) +NTFS stores all data in streams. Every file has exactly one unnamed +data stream and can have many named data streams. The size of a file is the +size of its unnamed data stream. By default, \fBntfs-3g\fR will only read +the unnamed data stream. +.PP +By using the options "streams_interface=windows", with the ntfs-3g driver +(not possible with lowntfs-3g), you will be able to read any named data +streams, simply by specifying the stream's name after a colon. +For example: +.RS +.sp +cat some.mp3:artist +.sp +.RE +Named data streams act like normal files, so you can read from them, write to +them and even delete them (using rm). You can list all the named data streams +a file has by getting the "ntfs.streams.list" extended attribute. +.SH OPTIONS +Below is a summary of the options that \fBntfs-3g\fR accepts. +.TP +\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP +Set the owner and the group of files and directories. The values are numerical. +The defaults are the uid and gid of the current process. +.TP +.BI umask= value +Set the bitmask of the file and directory permissions that are not +present. The value is given in octal. The default value is 0 which +means full access to everybody. +.TP +.BI fmask= value +Set the bitmask of the file permissions that are not present. +The value is given in octal. The default value is 0 which +means full access to everybody. +.TP +.BI dmask= value +Set the bitmask of the directory permissions that are not +present. The value is given in octal. The default value is 0 which +means full access to everybody. +.TP +.BI usermapping= file-name +Use file \fIfile-name\fP as the user mapping file instead of the default +\fB.NTFS-3G/UserMapping\fP. If \fIfile-name\fP defines a full path, the +file must be located on a partition previously mounted. If it defines a +relative path, it is interpreted relative to the root of NTFS partition +being mounted. +.P +.RS +When a user mapping file is defined, the options \fBuid=\fP, \fBgid=\fP, +\fBumask=\fP, \fBfmask=\fP, \fBdmask=\fP and \fBsilent\fP are ignored. +.RE +.TP +.B permissions +Set standard permissions on created files and use standard access control. +This option is set by default when a user mapping file is present. +.TP +.B acl +Enable setting Posix ACLs on created files and use them for access control. +This option is only available on specific builds. It is set by default +when a user mapping file is present and the +.B permissions +mount option is not set. +.TP +.B inherit +When creating a new file, set its initial protections +according to inheritance rules defined in parent directory. These rules +deviate from Posix specifications, but yield a better Windows +compatibility. The \fBpermissions\fR option or a valid user mapping file +is required for this option to be effective. +.TP +.B ro +Mount filesystem read\-only. Useful if Windows is hibernated or the +NTFS journal file is unclean. +.TP +.BI locale= value +This option can be useful when wanting a language specific locale environment. +It is however discouraged as it leads to files with untranslatable chars +to not be visible. +.TP +.B force +This option is obsolete. It has been superseded by the \fBrecover\fR and +\fBnorecover\fR options. +.TP +.B recover +Recover and try to mount a partition which was not unmounted properly by +Windows. The Windows logfile is cleared, which may cause inconsistencies. +Currently this is the default option. +.TP +.B norecover +Do not try to mount a partition which was not unmounted properly by Windows. +.TP +.B ignore_case \fP(only with lowntfs-3g) +Ignore character case when accessing a file (\fBFOO\fR, \fBFoo\fR, \fBfoo\fR, +etc. designate the same file). All files are displayed with lower case in +directory listings. +.TP +.B remove_hiberfile +Unlike in case of read-only mount, the read-write mount is denied if +the NTFS volume is hibernated. One needs either to resume Windows and +shutdown it properly, or use this option which will remove the Windows +hibernation file. Please note, this means that the saved Windows +session will be completely lost. Use this option under your own +responsibility. +.TP +.B atime, noatime, relatime +The +.B atime +option updates inode access time for each access. + +The +.B noatime +option disables inode access time updates which can speed up +file operations and prevent sleeping (notebook) disks spinning +up too often thus saving energy and disk lifetime. + +The +.B relatime +option is very similar to +.B noatime. +It updates inode access times relative to modify or change time. +The access time is only updated if the previous access time was earlier +than the current modify or change time. Unlike +.B noatime +this option doesn't break applications that need to know +if a file has been read since the last time it was modified. +This is the default behaviour. +.TP +.B delay_mtime[= value] +Only update the file modification time and the file change time of a file +when it is closed or when the indicated delay since the previous update has +elapsed. The argument is a number of seconds, with a default value of 60. +This is mainly useful for big files which are kept open for a long +time and written to without changing their size, such as databases or file +system images mounted as loop. +.TP +.B show_sys_files +Show the metafiles in directory listings. Otherwise the default behaviour is +to hide the metafiles, which are special files used to store the NTFS +structure. Please note that even when this option is specified, "$MFT" may +not be visible due to a glibc bug. Furthermore, irrespectively of +show_sys_files, all files are accessible by name, for example you can always +do +"ls \-l '$UpCase'". +.TP +.B hide_hid_files +Hide the hidden files and directories in directory listings, the hidden files +and directories being the ones whose NTFS attribute have the hidden flag set. +The hidden files will not be selected when using wildcards in commands, +but all files and directories remain accessible by full name, for example you +can always display the Windows trash bin directory by : +"ls \-ld '$RECYCLE.BIN'". +.TP +.B hide_dot_files +Set the hidden flag in the NTFS attribute for created files and directories +whose first character of the name is a dot. Such files and directories +normally do not appear in directory listings, and when the flag is set +they do not appear in Windows directory displays either. +When a file is renamed or linked with a new name, the hidden flag is +adjusted to the latest name. +.TP +.B windows_names +This option prevents files, directories and extended attributes to be +created with a name not allowed by windows, because +.RS +.RS +.sp +- it contains some not allowed character, +.br +- or the last character is a space or a dot, +.br +- or the name is reserved. +.sp +.RE +The forbidden characters are the nine characters " * / : < > ? \\ | and +those whose code is less than 0x20, and +the reserved names are CON, PRN, AUX, NUL, COM1..COM9, LPT1..LPT9, +with no suffix or followed by a dot. +.sp +Existing such files can still be read (and renamed). +.RE +.TP +.B allow_other +This option overrides the security measure restricting file access +to the user mounting the filesystem. This option is only +allowed to root, but this restriction can be overridden by +the 'user_allow_other' option in the /etc/fuse.conf file. +.TP +.BI max_read= value +With this option the maximum size of read operations can be set. +The default is infinite. Note that the size of read requests is +limited anyway to 32 pages (which is 128kbyte on i386). +.TP +.B silent +Do nothing, without returning any error, on chmod and chown operations +and on permission checking errors, +when the \fBpermissions\fR option is not set and no user mapping file +is defined. This option is on by default, and when set off (through option +\fBno_def_opts\fR) ownership and permissions parameters have to be set. +.TP +.B no_def_opts +By default ntfs-3g acts as if "silent" (ignore permission errors when +permissions are not enabled), +"allow_other" (allow any user to access files) and "nonempty" +(allow mounting on non-empty directories) were set, and "no_def_opts" +cancels these default options. +.TP +.BI streams_interface= value +This option controls how the user can access Alternate Data Streams (ADS) or +in other words, named data streams. It can be set to, one of \fBnone\fR, +\fBwindows\fR or \fBxattr\fR. If the option is set to \fBnone\fR, the user +will have no access to the named data streams. If it is set to \fBwindows\fR +(not possible with lowntfs-3g), then the user can access them just like in +Windows (eg. cat file:stream). If it's set to \fBxattr\fR, then the named +data streams are mapped to xattrs and user can manipulate them using +\fB{get,set}fattr\fR utilities. The default is \fBxattr\fR. +.TP +.B user_xattr +Same as \fBstreams_interface=\fP\fIxattr\fP. +.TP +.B efs_raw +This option should only be used in backup or restore situation. +It changes the apparent size of files and the behavior of read and +write operation so that encrypted files can be saved and restored +without being decrypted. The \fBuser.ntfs.efsinfo\fP extended attribute +has also to be saved and restored for the file to be decrypted. +.TP +.B compression +This option enables creating new transparently compressed files in +directories marked for compression. A directory is marked for compression by +setting the bit 11 (value 0x00000800) in its Windows attribute. In such a +directory, new files are created compressed and new subdirectories are +themselves marked for compression. The option and the flag have no effect +on existing files. Currently this is the default option. +.TP +.B nocompression +This option disables creating new transparently compressed files in directories +marked for compression. Existing compressed files can still be read and +updated. +.TP +.B big_writes +This option prevents fuse from splitting write buffers into 4K chunks, +enabling big write buffers to be transferred from the application in a +single step (up to some system limit, generally 128K bytes). +.TP +.B debug +Makes ntfs-3g to print a lot of debug output from libntfs-3g and FUSE. +.TP +.B no_detach +Makes ntfs-3g to not detach from terminal and print some debug output. +.SH USER MAPPING +NTFS uses specific ids to record the ownership of files instead of +the \fBuid\fP and \fBgid\fP used by Linux. As a consequence a mapping +between the ids has to be defined for ownerships to be recorded into +NTFS and recognized. +.P +By default, this mapping is fetched from the file \fB.NTFS-3G/UserMapping\fP +located in the NTFS partition. The option \fBusermapping=\fP may be used +to define another location. When the option permissions is set and +no mapping file is found, a default mapping is used. +.P +Each line in the user mapping file defines a mapping. It is organized +in three fields separated by colons. The first field identifies a \fBuid\fP, +the second field identifies a \fBgid\fP and the third one identifies the +corresponding NTFS id, known as a \fBSID\fP. The \fBuid\fP and the \fBgid\fP +are optional and defining both of them for the same \fBSID\fP is not +recommended. +.P +If no interoperation with Windows is needed, you can use the option +\fBpermissions\fP to define a standard mapping. Alternately, you may define +your own mapping by setting a single default mapping with no uid and gid. In +both cases, files created on Linux will appear to Windows as owned by a +foreign user, and files created on Windows will appear to Linux as owned by +root. Just copy the example below and replace the 9 and 10-digit numbers by +any number not greater than 4294967295. The resulting behavior is the same as +the one with the option permission set with no ownership option and no user +mapping file available. +.RS +.sp +.B ::S-1-5-21-3141592653-589793238-462643383-10000 +.sp +.RE +If a strong interoperation with Windows is needed, the mapping has to be +defined for each user and group known in both system, and the \fBSID\fPs used +by Windows has to be collected. This will lead to a user mapping file like : +.RS +.sp +.B john::S-1-5-21-3141592653-589793238-462643383-1008 +.B mary::S-1-5-21-3141592653-589793238-462643383-1009 +.B :smith:S-1-5-21-3141592653-589793238-462643383-513 +.B ::S-1-5-21-3141592653-589793238-462643383-10000 +.sp +.RE +.P +The utility \fBntfs-3g.usermap\fP may be used to create such a user +mapping file. +.SH EXAMPLES +Mount /dev/sda1 to /mnt/windows: +.RS +.sp +.B ntfs-3g /dev/sda1 /mnt/windows +.RE +or +.RS +.B mount -t ntfs-3g /dev/sda1 /mnt/windows +.sp +.RE +Mount the ntfs data partition /dev/sda3 to /mnt/data with standard Linux +permissions applied : +.RS +.sp +.B ntfs-3g -o permissions /dev/sda3 /mnt/data +.RE +or +.RS +.B mount -t ntfs-3g -o permissions /dev/sda3 /mnt/data +.sp +.RE +Read\-only mount /dev/sda5 to /home/user/mnt and make user with uid 1000 +to be the owner of all files: +.RS +.sp +.B ntfs-3g /dev/sda5 /home/user/mnt \-o ro,uid=1000 +.sp +.RE +/etc/fstab entry for the above (the sixth and last field has to be zero to +avoid a file system check at boot time) : +.RS +.sp +.B /dev/sda5 /home/user/mnt ntfs\-3g ro,uid=1000 0 0 +.sp +.RE +Unmount /mnt/windows: +.RS +.sp +.B umount /mnt/windows +.sp +.RE +.SH EXIT CODES +To facilitate the use of the +.B ntfs-3g +driver in scripts, an exit code is returned to give an indication of the +mountability status of a volume. Value 0 means success, and all other +ones mean an error. The unique error codes are documented in the +.BR ntfs-3g.probe (8) +manual page. +.SH KNOWN ISSUES +Please see +.RS +.sp +http://www.tuxera.com/support/ +.sp +.RE +for common questions and known issues. +If you would find a new one in the latest release of +the software then please send an email describing it +in detail. You can contact the +development team on the ntfs\-3g\-devel@lists.sf.net +address. +.SH AUTHORS +.B ntfs-3g +was based on and a major improvement to ntfsmount and libntfs which were +written by Yura Pakhuchiy and the Linux-NTFS team. The improvements were +made, the ntfs-3g project was initiated and currently led by long time +Linux-NTFS team developer Szabolcs Szakacsits (szaka@tuxera.com). +.SH THANKS +Several people made heroic efforts, often over five or more +years which resulted the ntfs-3g driver. Most importantly they are +Anton Altaparmakov, Jean-Pierre André, Richard Russon, Szabolcs Szakacsits, +Yura Pakhuchiy, Yuval Fledel, and the author of the groundbreaking FUSE +filesystem development framework, Miklos Szeredi. +.SH SEE ALSO +.BR ntfs-3g.probe (8), +.BR ntfsprogs (8), +.BR attr (5), +.BR getfattr (1) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in new file mode 100755 index 0000000000000000000000000000000000000000..57fffc962c9571e89da17b295a1bae738c0833bb --- /dev/null +++ b/src/ntfs-3g.8.in @@ -0,0 +1,465 @@ +.\" Copyright (c) 2005-2006 Yura Pakhuchiy. +.\" Copyright (c) 2005 Richard Russon. +.\" Copyright (c) 2006-2009 Szabolcs Szakacsits. +.\" Copyright (c) 2009-2014 Jean-Pierre Andre +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFS-3G 8 "Mar 2014" "ntfs-3g @VERSION@" +.SH NAME +ntfs-3g \- Third Generation Read/Write NTFS Driver +.SH SYNOPSIS +.B ntfs-3g +\fB[-o \fIoption\fP\fB[,...]]\fR +.I volume mount_point +.br +.B mount \-t ntfs-3g +\fB[-o \fIoption\fP\fB[,...]]\fR +.I volume mount_point +.br +.B lowntfs-3g +\fB[-o \fIoption\fP\fB[,...]]\fR +.I volume mount_point +.br +.B mount \-t lowntfs-3g +\fB[-o \fIoption\fP\fB[,...]]\fR +.I volume mount_point +.SH DESCRIPTION +\fBntfs-3g\fR is an NTFS driver, which can create, remove, rename, move +files, directories, hard links, and streams; it can read and write files, +including streams, sparse files and transparently compressed files; it can +handle special files like symbolic links, devices, and FIFOs; moreover it +provides standard management of file ownership and permissions, including +POSIX ACLs. +.PP +It comes in two variants \fBntfs-3g\fR and \fBlowntfs-3g\fR with +a few differences mentioned below in relevant options descriptions. +.PP +The \fIvolume\fR to be mounted can be either a block device or +an image file. +.SS Windows hibernation and fast restarting +On computers which can be dual-booted into Windows or Linux, Windows has +to be fully shut down before booting into Linux, otherwise the NTFS file +systems on internal disks may be left in an inconsistent state and changes +made by Linux may be ignored by Windows. +.P +So, Windows may not be left in hibernation when starting Linux, in order +to avoid inconsistencies. Moreover, the fast restart feature available on +recent Windows systems has to be disabled. This can be achieved by issuing +as an Administrator the Windows command which disables both +hibernation and fast restarting : +.RS +.sp +powercfg /h off +.sp +.RE +.SS Access Handling and Security +By default, files and directories are owned by the effective +user and group of the mounting process, and everybody has +full read, write, execution and directory browsing permissions. +You can also assign permissions to a single user by using the +.B uid +and/or the +.B gid +options together with the +.B umask, +or +.B fmask +and +.B dmask +options. +.PP +Doing so, Windows users have full access to the files created by +.B ntfs-3g. +.PP +But, by setting the \fBpermissions\fR option, you can benefit from the full +ownership and permissions features as defined by POSIX. Moreover, by defining +a Windows-to-Linux user mapping, the ownerships and permissions are even +applied to Windows users and conversely. +.PP +If +.B ntfs-3g +is set setuid-root then non-root users will +be also able to mount volumes. +.SS Windows Filename Compatibility +NTFS supports several filename namespaces: DOS, Win32 and POSIX. While the +\fBntfs-3g\fR driver handles all of them, it always creates new files in the +POSIX namespace for maximum portability and interoperability reasons. +This means that filenames are case sensitive and all characters are +allowed except '/' and '\\0'. This is perfectly legal on Windows, though +some application may get confused. The option \fBwindows_names\fP may be +used to apply Windows restrictions to new file names. +.SS Alternate Data Streams (ADS) +NTFS stores all data in streams. Every file has exactly one unnamed +data stream and can have many named data streams. The size of a file is the +size of its unnamed data stream. By default, \fBntfs-3g\fR will only read +the unnamed data stream. +.PP +By using the options "streams_interface=windows", with the ntfs-3g driver +(not possible with lowntfs-3g), you will be able to read any named data +streams, simply by specifying the stream's name after a colon. +For example: +.RS +.sp +cat some.mp3:artist +.sp +.RE +Named data streams act like normal files, so you can read from them, write to +them and even delete them (using rm). You can list all the named data streams +a file has by getting the "ntfs.streams.list" extended attribute. +.SH OPTIONS +Below is a summary of the options that \fBntfs-3g\fR accepts. +.TP +\fBuid=\fP\fIvalue\fP and \fBgid=\fP\fIvalue\fP +Set the owner and the group of files and directories. The values are numerical. +The defaults are the uid and gid of the current process. +.TP +.BI umask= value +Set the bitmask of the file and directory permissions that are not +present. The value is given in octal. The default value is 0 which +means full access to everybody. +.TP +.BI fmask= value +Set the bitmask of the file permissions that are not present. +The value is given in octal. The default value is 0 which +means full access to everybody. +.TP +.BI dmask= value +Set the bitmask of the directory permissions that are not +present. The value is given in octal. The default value is 0 which +means full access to everybody. +.TP +.BI usermapping= file-name +Use file \fIfile-name\fP as the user mapping file instead of the default +\fB.NTFS-3G/UserMapping\fP. If \fIfile-name\fP defines a full path, the +file must be located on a partition previously mounted. If it defines a +relative path, it is interpreted relative to the root of NTFS partition +being mounted. +.P +.RS +When a user mapping file is defined, the options \fBuid=\fP, \fBgid=\fP, +\fBumask=\fP, \fBfmask=\fP, \fBdmask=\fP and \fBsilent\fP are ignored. +.RE +.TP +.B permissions +Set standard permissions on created files and use standard access control. +This option is set by default when a user mapping file is present. +.TP +.B acl +Enable setting Posix ACLs on created files and use them for access control. +This option is only available on specific builds. It is set by default +when a user mapping file is present and the +.B permissions +mount option is not set. +.TP +.B inherit +When creating a new file, set its initial protections +according to inheritance rules defined in parent directory. These rules +deviate from Posix specifications, but yield a better Windows +compatibility. The \fBpermissions\fR option or a valid user mapping file +is required for this option to be effective. +.TP +.B ro +Mount filesystem read\-only. Useful if Windows is hibernated or the +NTFS journal file is unclean. +.TP +.BI locale= value +This option can be useful when wanting a language specific locale environment. +It is however discouraged as it leads to files with untranslatable chars +to not be visible. +.TP +.B force +This option is obsolete. It has been superseded by the \fBrecover\fR and +\fBnorecover\fR options. +.TP +.B recover +Recover and try to mount a partition which was not unmounted properly by +Windows. The Windows logfile is cleared, which may cause inconsistencies. +Currently this is the default option. +.TP +.B norecover +Do not try to mount a partition which was not unmounted properly by Windows. +.TP +.B ignore_case \fP(only with lowntfs-3g) +Ignore character case when accessing a file (\fBFOO\fR, \fBFoo\fR, \fBfoo\fR, +etc. designate the same file). All files are displayed with lower case in +directory listings. +.TP +.B remove_hiberfile +Unlike in case of read-only mount, the read-write mount is denied if +the NTFS volume is hibernated. One needs either to resume Windows and +shutdown it properly, or use this option which will remove the Windows +hibernation file. Please note, this means that the saved Windows +session will be completely lost. Use this option under your own +responsibility. +.TP +.B atime, noatime, relatime +The +.B atime +option updates inode access time for each access. + +The +.B noatime +option disables inode access time updates which can speed up +file operations and prevent sleeping (notebook) disks spinning +up too often thus saving energy and disk lifetime. + +The +.B relatime +option is very similar to +.B noatime. +It updates inode access times relative to modify or change time. +The access time is only updated if the previous access time was earlier +than the current modify or change time. Unlike +.B noatime +this option doesn't break applications that need to know +if a file has been read since the last time it was modified. +This is the default behaviour. +.TP +.B delay_mtime[= value] +Only update the file modification time and the file change time of a file +when it is closed or when the indicated delay since the previous update has +elapsed. The argument is a number of seconds, with a default value of 60. +This is mainly useful for big files which are kept open for a long +time and written to without changing their size, such as databases or file +system images mounted as loop. +.TP +.B show_sys_files +Show the metafiles in directory listings. Otherwise the default behaviour is +to hide the metafiles, which are special files used to store the NTFS +structure. Please note that even when this option is specified, "$MFT" may +not be visible due to a glibc bug. Furthermore, irrespectively of +show_sys_files, all files are accessible by name, for example you can always +do +"ls \-l '$UpCase'". +.TP +.B hide_hid_files +Hide the hidden files and directories in directory listings, the hidden files +and directories being the ones whose NTFS attribute have the hidden flag set. +The hidden files will not be selected when using wildcards in commands, +but all files and directories remain accessible by full name, for example you +can always display the Windows trash bin directory by : +"ls \-ld '$RECYCLE.BIN'". +.TP +.B hide_dot_files +Set the hidden flag in the NTFS attribute for created files and directories +whose first character of the name is a dot. Such files and directories +normally do not appear in directory listings, and when the flag is set +they do not appear in Windows directory displays either. +When a file is renamed or linked with a new name, the hidden flag is +adjusted to the latest name. +.TP +.B windows_names +This option prevents files, directories and extended attributes to be +created with a name not allowed by windows, because +.RS +.RS +.sp +- it contains some not allowed character, +.br +- or the last character is a space or a dot, +.br +- or the name is reserved. +.sp +.RE +The forbidden characters are the nine characters " * / : < > ? \\ | and +those whose code is less than 0x20, and +the reserved names are CON, PRN, AUX, NUL, COM1..COM9, LPT1..LPT9, +with no suffix or followed by a dot. +.sp +Existing such files can still be read (and renamed). +.RE +.TP +.B allow_other +This option overrides the security measure restricting file access +to the user mounting the filesystem. This option is only +allowed to root, but this restriction can be overridden by +the 'user_allow_other' option in the /etc/fuse.conf file. +.TP +.BI max_read= value +With this option the maximum size of read operations can be set. +The default is infinite. Note that the size of read requests is +limited anyway to 32 pages (which is 128kbyte on i386). +.TP +.B silent +Do nothing, without returning any error, on chmod and chown operations +and on permission checking errors, +when the \fBpermissions\fR option is not set and no user mapping file +is defined. This option is on by default, and when set off (through option +\fBno_def_opts\fR) ownership and permissions parameters have to be set. +.TP +.B no_def_opts +By default ntfs-3g acts as if "silent" (ignore permission errors when +permissions are not enabled), +"allow_other" (allow any user to access files) and "nonempty" +(allow mounting on non-empty directories) were set, and "no_def_opts" +cancels these default options. +.TP +.BI streams_interface= value +This option controls how the user can access Alternate Data Streams (ADS) or +in other words, named data streams. It can be set to, one of \fBnone\fR, +\fBwindows\fR or \fBxattr\fR. If the option is set to \fBnone\fR, the user +will have no access to the named data streams. If it is set to \fBwindows\fR +(not possible with lowntfs-3g), then the user can access them just like in +Windows (eg. cat file:stream). If it's set to \fBxattr\fR, then the named +data streams are mapped to xattrs and user can manipulate them using +\fB{get,set}fattr\fR utilities. The default is \fBxattr\fR. +.TP +.B user_xattr +Same as \fBstreams_interface=\fP\fIxattr\fP. +.TP +.B efs_raw +This option should only be used in backup or restore situation. +It changes the apparent size of files and the behavior of read and +write operation so that encrypted files can be saved and restored +without being decrypted. The \fBuser.ntfs.efsinfo\fP extended attribute +has also to be saved and restored for the file to be decrypted. +.TP +.B compression +This option enables creating new transparently compressed files in +directories marked for compression. A directory is marked for compression by +setting the bit 11 (value 0x00000800) in its Windows attribute. In such a +directory, new files are created compressed and new subdirectories are +themselves marked for compression. The option and the flag have no effect +on existing files. Currently this is the default option. +.TP +.B nocompression +This option disables creating new transparently compressed files in directories +marked for compression. Existing compressed files can still be read and +updated. +.TP +.B big_writes +This option prevents fuse from splitting write buffers into 4K chunks, +enabling big write buffers to be transferred from the application in a +single step (up to some system limit, generally 128K bytes). +.TP +.B debug +Makes ntfs-3g to print a lot of debug output from libntfs-3g and FUSE. +.TP +.B no_detach +Makes ntfs-3g to not detach from terminal and print some debug output. +.SH USER MAPPING +NTFS uses specific ids to record the ownership of files instead of +the \fBuid\fP and \fBgid\fP used by Linux. As a consequence a mapping +between the ids has to be defined for ownerships to be recorded into +NTFS and recognized. +.P +By default, this mapping is fetched from the file \fB.NTFS-3G/UserMapping\fP +located in the NTFS partition. The option \fBusermapping=\fP may be used +to define another location. When the option permissions is set and +no mapping file is found, a default mapping is used. +.P +Each line in the user mapping file defines a mapping. It is organized +in three fields separated by colons. The first field identifies a \fBuid\fP, +the second field identifies a \fBgid\fP and the third one identifies the +corresponding NTFS id, known as a \fBSID\fP. The \fBuid\fP and the \fBgid\fP +are optional and defining both of them for the same \fBSID\fP is not +recommended. +.P +If no interoperation with Windows is needed, you can use the option +\fBpermissions\fP to define a standard mapping. Alternately, you may define +your own mapping by setting a single default mapping with no uid and gid. In +both cases, files created on Linux will appear to Windows as owned by a +foreign user, and files created on Windows will appear to Linux as owned by +root. Just copy the example below and replace the 9 and 10-digit numbers by +any number not greater than 4294967295. The resulting behavior is the same as +the one with the option permission set with no ownership option and no user +mapping file available. +.RS +.sp +.B ::S-1-5-21-3141592653-589793238-462643383-10000 +.sp +.RE +If a strong interoperation with Windows is needed, the mapping has to be +defined for each user and group known in both system, and the \fBSID\fPs used +by Windows has to be collected. This will lead to a user mapping file like : +.RS +.sp +.B john::S-1-5-21-3141592653-589793238-462643383-1008 +.B mary::S-1-5-21-3141592653-589793238-462643383-1009 +.B :smith:S-1-5-21-3141592653-589793238-462643383-513 +.B ::S-1-5-21-3141592653-589793238-462643383-10000 +.sp +.RE +.P +The utility \fBntfs-3g.usermap\fP may be used to create such a user +mapping file. +.SH EXAMPLES +Mount /dev/sda1 to /mnt/windows: +.RS +.sp +.B ntfs-3g /dev/sda1 /mnt/windows +.RE +or +.RS +.B mount -t ntfs-3g /dev/sda1 /mnt/windows +.sp +.RE +Mount the ntfs data partition /dev/sda3 to /mnt/data with standard Linux +permissions applied : +.RS +.sp +.B ntfs-3g -o permissions /dev/sda3 /mnt/data +.RE +or +.RS +.B mount -t ntfs-3g -o permissions /dev/sda3 /mnt/data +.sp +.RE +Read\-only mount /dev/sda5 to /home/user/mnt and make user with uid 1000 +to be the owner of all files: +.RS +.sp +.B ntfs-3g /dev/sda5 /home/user/mnt \-o ro,uid=1000 +.sp +.RE +/etc/fstab entry for the above (the sixth and last field has to be zero to +avoid a file system check at boot time) : +.RS +.sp +.B /dev/sda5 /home/user/mnt ntfs\-3g ro,uid=1000 0 0 +.sp +.RE +Unmount /mnt/windows: +.RS +.sp +.B umount /mnt/windows +.sp +.RE +.SH EXIT CODES +To facilitate the use of the +.B ntfs-3g +driver in scripts, an exit code is returned to give an indication of the +mountability status of a volume. Value 0 means success, and all other +ones mean an error. The unique error codes are documented in the +.BR ntfs-3g.probe (8) +manual page. +.SH KNOWN ISSUES +Please see +.RS +.sp +http://www.tuxera.com/support/ +.sp +.RE +for common questions and known issues. +If you would find a new one in the latest release of +the software then please send an email describing it +in detail. You can contact the +development team on the ntfs\-3g\-devel@lists.sf.net +address. +.SH AUTHORS +.B ntfs-3g +was based on and a major improvement to ntfsmount and libntfs which were +written by Yura Pakhuchiy and the Linux-NTFS team. The improvements were +made, the ntfs-3g project was initiated and currently led by long time +Linux-NTFS team developer Szabolcs Szakacsits (szaka@tuxera.com). +.SH THANKS +Several people made heroic efforts, often over five or more +years which resulted the ntfs-3g driver. Most importantly they are +Anton Altaparmakov, Jean-Pierre André, Richard Russon, Szabolcs Szakacsits, +Yura Pakhuchiy, Yuval Fledel, and the author of the groundbreaking FUSE +filesystem development framework, Miklos Szeredi. +.SH SEE ALSO +.BR ntfs-3g.probe (8), +.BR ntfsprogs (8), +.BR attr (5), +.BR getfattr (1) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c new file mode 100755 index 0000000000000000000000000000000000000000..de0f920eeee023294d50f07edd3d75fb9f17ae3a --- /dev/null +++ b/src/ntfs-3g.c @@ -0,0 +1,4013 @@ +/** + * ntfs-3g - Third Generation NTFS Driver + * + * Copyright (c) 2005-2007 Yura Pakhuchiy + * Copyright (c) 2005 Yuval Fledel + * Copyright (c) 2006-2009 Szabolcs Szakacsits + * Copyright (c) 2007-2015 Jean-Pierre Andre + * Copyright (c) 2009 Erik Larsson + * + * This file is originated from the Linux-NTFS project. + * + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include <fuse.h> + +#if !defined(FUSE_VERSION) || (FUSE_VERSION < 26) +#error "***********************************************************" +#error "* *" +#error "* Compilation requires at least FUSE version 2.6.0! *" +#error "* *" +#error "***********************************************************" +#endif + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#include <signal.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#include <syslog.h> +#include <sys/wait.h> + +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_MKDEV_H +#include <sys/mkdev.h> +#endif + +#if defined(__APPLE__) || defined(__DARWIN__) +#include <sys/dirent.h> +#elif defined(__sun) && defined (__SVR4) +#include <sys/param.h> +#endif /* defined(__APPLE__) || defined(__DARWIN__), ... */ + +#include "compat.h" +#include "attrib.h" +#include "inode.h" +#include "volume.h" +#include "dir.h" +#include "unistr.h" +#include "layout.h" +#include "index.h" +#include "ntfstime.h" +#include "security.h" +#include "reparse.h" +#include "object_id.h" +#include "efs.h" +#include "logging.h" +#include "xattrs.h" +#include "misc.h" +#include "ioctl.h" + +#include "ntfs-3g_common.h" + +/* + * The following permission checking modes are governed by + * the HPERMSCONFIG value in param.h + */ + +/* ACLS may be checked by kernel (requires a fuse patch) or here */ +#define KERNELACLS ((HPERMSCONFIG > 6) & (HPERMSCONFIG < 10)) +/* basic permissions may be checked by kernel or here */ +#define KERNELPERMS (((HPERMSCONFIG - 1) % 6) < 3) +/* may want to use fuse/kernel cacheing */ +#define CACHEING (!(HPERMSCONFIG % 3)) + +#if KERNELACLS & !KERNELPERMS +#error Incompatible options KERNELACLS and KERNELPERMS +#endif + + /* sometimes the kernel cannot check access */ +#define ntfs_real_allowed_access(scx, ni, type) ntfs_allowed_access(scx, ni, type) +#if POSIXACLS & KERNELPERMS & !KERNELACLS + /* short-circuit if PERMS checked by kernel and ACLs by fs */ +#define ntfs_allowed_access(scx, ni, type) \ + ((scx)->vol->secure_flags & (1 << SECURITY_DEFAULT) \ + ? 1 : ntfs_allowed_access(scx, ni, type)) +#endif + +#define set_archive(ni) (ni)->flags |= FILE_ATTR_ARCHIVE + +typedef enum { + FSTYPE_NONE, + FSTYPE_UNKNOWN, + FSTYPE_FUSE, + FSTYPE_FUSEBLK +} fuse_fstype; + +typedef struct { + fuse_fill_dir_t filler; + void *buf; +} ntfs_fuse_fill_context_t; + +enum { + CLOSE_COMPRESSED = 1, + CLOSE_ENCRYPTED = 2, + CLOSE_DMTIME = 4 +}; + +static struct ntfs_options opts; + +const char *EXEC_NAME = "ntfs-3g"; + +static ntfs_fuse_context_t *ctx; +static u32 ntfs_sequence; + +static const char *usage_msg = +"\n" +"%s %s %s %d - Third Generation NTFS Driver\n" +"\t\tConfiguration type %d, " +#ifdef HAVE_SETXATTR +"XATTRS are on, " +#else +"XATTRS are off, " +#endif +#if POSIXACLS +"POSIX ACLS are on\n" +#else +"POSIX ACLS are off\n" +#endif +"\n" +"Copyright (C) 2005-2007 Yura Pakhuchiy\n" +"Copyright (C) 2006-2009 Szabolcs Szakacsits\n" +"Copyright (C) 2007-2015 Jean-Pierre Andre\n" +"Copyright (C) 2009 Erik Larsson\n" +"\n" +"Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n" +"\n" +"Options: ro (read-only mount), windows_names, uid=, gid=,\n" +" umask=, fmask=, dmask=, streams_interface=.\n" +" Please see the details in the manual (type: man ntfs-3g).\n" +"\n" +"Example: ntfs-3g /dev/sda1 /mnt/windows\n" +"\n" +"%s"; + +static const char ntfs_bad_reparse[] = "unsupported reparse point"; + +#ifdef FUSE_INTERNAL +int drop_privs(void); +int restore_privs(void); +#else +/* + * setuid and setgid root ntfs-3g denies to start with external FUSE, + * therefore the below functions are no-op in such case. + */ +static int drop_privs(void) { return 0; } +#if defined(linux) || defined(__uClinux__) +static int restore_privs(void) { return 0; } +#endif + +static const char *setuid_msg = +"Mount is denied because setuid and setgid root ntfs-3g is insecure with the\n" +"external FUSE library. Either remove the setuid/setgid bit from the binary\n" +"or rebuild NTFS-3G with integrated FUSE support and make it setuid root.\n" +"Please see more information at\n" +"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; + +static const char *unpriv_fuseblk_msg = +"Unprivileged user can not mount NTFS block devices using the external FUSE\n" +"library. Either mount the volume as root, or rebuild NTFS-3G with integrated\n" +"FUSE support and make it setuid root. Please see more information at\n" +"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n"; +#endif + + +/** + * ntfs_fuse_is_named_data_stream - check path to be to named data stream + * @path: path to check + * + * Returns 1 if path is to named data stream or 0 otherwise. + */ +static int ntfs_fuse_is_named_data_stream(const char *path) +{ + if (strchr(path, ':') && ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) + return 1; + return 0; +} + +static void ntfs_fuse_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) +{ + if (ctx->atime == ATIME_DISABLED) + mask &= ~NTFS_UPDATE_ATIME; + else if (ctx->atime == ATIME_RELATIVE && mask == NTFS_UPDATE_ATIME && + (le64_to_cpu(ni->last_access_time) + >= le64_to_cpu(ni->last_data_change_time)) && + (le64_to_cpu(ni->last_access_time) + >= le64_to_cpu(ni->last_mft_change_time))) + return; + ntfs_inode_update_times(ni, mask); +} + +static s64 ntfs_get_nr_free_mft_records(ntfs_volume *vol) +{ + ntfs_attr *na = vol->mftbmp_na; + s64 nr_free = ntfs_attr_get_free_bits(na); + + if (nr_free >= 0) + nr_free += (na->allocated_size - na->data_size) << 3; + return nr_free; +} + +/* + * Fill a security context as needed by security functions + * returns TRUE if there is a user mapping, + * FALSE if there is none + * This is not an error and the context is filled anyway, + * it is used for implicit Windows-like inheritance + */ + +static BOOL ntfs_fuse_fill_security_context(struct SECURITY_CONTEXT *scx) +{ + struct fuse_context *fusecontext; + + scx->vol = ctx->vol; + scx->mapping[MAPUSERS] = ctx->security.mapping[MAPUSERS]; + scx->mapping[MAPGROUPS] = ctx->security.mapping[MAPGROUPS]; + scx->pseccache = &ctx->seccache; + fusecontext = fuse_get_context(); + scx->uid = fusecontext->uid; + scx->gid = fusecontext->gid; + scx->tid = fusecontext->pid; +#ifdef FUSE_CAP_DONT_MASK + /* the umask can be processed by the file system */ + scx->umask = fusecontext->umask; +#else + /* the umask if forced by fuse on creation */ + scx->umask = 0; +#endif + + return (ctx->security.mapping[MAPUSERS] != (struct MAPPING*)NULL); +} + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + +/* + * Check access to parent directory + * + * directory and file inodes are only opened when not fed in, + * they *HAVE TO* be fed in when already open, however + * file inode is only useful when S_ISVTX is requested + * + * returns 1 if allowed, + * 0 if not allowed or some error occurred (errno tells why) + */ + +static int ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, + const char *path, ntfs_inode *dir_ni, + ntfs_inode *ni, mode_t accesstype) +{ + int allowed; + ntfs_inode *ni2; + ntfs_inode *dir_ni2; + char *dirpath; + char *name; + struct stat stbuf; + +#if POSIXACLS & KERNELPERMS & !KERNELACLS + /* short-circuit if PERMS checked by kernel and ACLs by fs */ + if (scx->vol->secure_flags & (1 << SECURITY_DEFAULT)) + allowed = 1; + else +#endif + if (dir_ni) + allowed = ntfs_real_allowed_access(scx, dir_ni, + accesstype); + else { + allowed = 0; + dirpath = strdup(path); + if (dirpath) { + /* the root of file system is seen as a parent of itself */ + /* is that correct ? */ + name = strrchr(dirpath, '/'); + *name = 0; + dir_ni2 = ntfs_pathname_to_inode(scx->vol, + NULL, dirpath); + if (dir_ni2) { + allowed = ntfs_real_allowed_access(scx, + dir_ni2, accesstype); + if (ntfs_inode_close(dir_ni2)) + allowed = 0; + } + free(dirpath); + } + } + /* + * for a not-owned sticky directory, have to + * check whether file itself is owned + */ + if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX)) + && (allowed == 2)) { + if (ni) + ni2 = ni; + else + ni2 = ntfs_pathname_to_inode(scx->vol, NULL, + path); + allowed = 0; + if (ni2) { + allowed = (ntfs_get_owner_mode(scx,ni2,&stbuf) + >= 0) + && (stbuf.st_uid == scx->uid); + if (!ni) + ntfs_inode_close(ni2); + } + } + return (allowed); +} + +#endif + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Check access to parent directory + * + * for non-standard cases where access control cannot be checked by kernel + * + * no known situations where S_ISVTX is requested + * + * returns 1 if allowed, + * 0 if not allowed or some error occurred (errno tells why) + */ + +static int ntfs_allowed_real_dir_access(struct SECURITY_CONTEXT *scx, + const char *path, ntfs_inode *dir_ni, + mode_t accesstype) +{ + int allowed; + ntfs_inode *dir_ni2; + char *dirpath; + char *name; + + if (dir_ni) + allowed = ntfs_real_allowed_access(scx, dir_ni, accesstype); + else { + allowed = 0; + dirpath = strdup(path); + if (dirpath) { + /* the root of file system is seen as a parent of itself */ + /* is that correct ? */ + name = strrchr(dirpath, '/'); + *name = 0; + dir_ni2 = ntfs_pathname_to_inode(scx->vol, NULL, + dirpath); + if (dir_ni2) { + allowed = ntfs_real_allowed_access(scx, + dir_ni2, accesstype); + if (ntfs_inode_close(dir_ni2)) + allowed = 0; + } + free(dirpath); + } + } + return (allowed); +} + +static ntfs_inode *get_parent_dir(const char *path) +{ + ntfs_inode *dir_ni; + char *dirpath; + char *p; + + dirpath = strdup(path); + dir_ni = (ntfs_inode*)NULL; + if (dirpath) { + p = strrchr(dirpath,'/'); + if (p) { /* always present, be safe */ + *p = 0; + dir_ni = ntfs_pathname_to_inode(ctx->vol, + NULL, dirpath); + } + free(dirpath); + } else + errno = ENOMEM; + return (dir_ni); +} + + +#endif /* HAVE_SETXATTR */ + +/** + * ntfs_fuse_statfs - return information about mounted NTFS volume + * @path: ignored (but fuse requires it) + * @sfs: statfs structure in which to return the information + * + * Return information about the mounted NTFS volume @sb in the statfs structure + * pointed to by @sfs (this is initialized with zeros before ntfs_statfs is + * called). We interpret the values to be correct of the moment in time at + * which we are called. Most values are variable otherwise and this isn't just + * the free values but the totals as well. For example we can increase the + * total number of file nodes if we run out and we can keep doing this until + * there is no more space on the volume left at all. + * + * This code based on ntfs_statfs from ntfs kernel driver. + * + * Returns 0 on success or -errno on error. + */ +static int ntfs_fuse_statfs(const char *path __attribute__((unused)), + struct statvfs *sfs) +{ + s64 size; + int delta_bits; + ntfs_volume *vol; + + vol = ctx->vol; + if (!vol) + return -ENODEV; + + /* + * File system block size. Used to calculate used/free space by df. + * Incorrectly documented as "optimal transfer block size". + */ + sfs->f_bsize = vol->cluster_size; + + /* Fundamental file system block size, used as the unit. */ + sfs->f_frsize = vol->cluster_size; + + /* + * Total number of blocks on file system in units of f_frsize. + * Since inodes are also stored in blocks ($MFT is a file) hence + * this is the number of clusters on the volume. + */ + sfs->f_blocks = vol->nr_clusters; + + /* Free blocks available for all and for non-privileged processes. */ + size = vol->free_clusters; + if (size < 0) + size = 0; + sfs->f_bavail = sfs->f_bfree = size; + + /* Free inodes on the free space */ + delta_bits = vol->cluster_size_bits - vol->mft_record_size_bits; + if (delta_bits >= 0) + size <<= delta_bits; + else + size >>= -delta_bits; + + /* Number of inodes at this point in time. */ + sfs->f_files = (vol->mftbmp_na->allocated_size << 3) + size; + + /* Free inodes available for all and for non-privileged processes. */ + size += vol->free_mft_records; + if (size < 0) + size = 0; + sfs->f_ffree = sfs->f_favail = size; + + /* Maximum length of filenames. */ + sfs->f_namemax = NTFS_MAX_NAME_LEN; + return 0; +} + +/** + * ntfs_fuse_parse_path - split path to path and stream name. + * @org_path: path to split + * @path: pointer to buffer in which parsed path saved + * @stream_name: pointer to buffer where stream name in unicode saved + * + * This function allocates buffers for @*path and @*stream, user must free them + * after use. + * + * Return values: + * <0 Error occurred, return -errno; + * 0 No stream name, @*stream is not allocated and set to AT_UNNAMED. + * >0 Stream name length in unicode characters. + */ +static int ntfs_fuse_parse_path(const char *org_path, char **path, + ntfschar **stream_name) +{ + char *stream_name_mbs; + int res; + + stream_name_mbs = strdup(org_path); + if (!stream_name_mbs) + return -errno; + if (ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) { + *path = strsep(&stream_name_mbs, ":"); + if (stream_name_mbs) { + *stream_name = NULL; + res = ntfs_mbstoucs(stream_name_mbs, stream_name); + if (res < 0) { + free(*path); + *path = NULL; + return -errno; + } + return res; + } + } else + *path = stream_name_mbs; + *stream_name = AT_UNNAMED; + return 0; +} + +static void set_fuse_error(int *err) +{ + if (!*err) + *err = -errno; +} + +#if defined(__APPLE__) || defined(__DARWIN__) +static int ntfs_macfuse_getxtimes(const char *org_path, + struct timespec *bkuptime, struct timespec *crtime) +{ + int res = 0; + ntfs_inode *ni; + char *path = NULL; + ntfschar *stream_name; + int stream_name_len; + + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); + if (stream_name_len < 0) + return stream_name_len; + memset(bkuptime, 0, sizeof(struct timespec)); + memset(crtime, 0, sizeof(struct timespec)); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) { + res = -errno; + goto exit; + } + + /* We have no backup timestamp in NTFS. */ + crtime->tv_sec = ni->creation_time; +exit: + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + free(path); + if (stream_name_len) + free(stream_name); + return res; +} + +int ntfs_macfuse_setcrtime(const char *path, const struct timespec *tv) +{ + ntfs_inode *ni; + int res = 0; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + if (tv) { + ni->creation_time = tv->tv_sec; + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + } + + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +int ntfs_macfuse_setbkuptime(const char *path, const struct timespec *tv) +{ + ntfs_inode *ni; + int res = 0; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + /* + * Only pretending to set backup time successfully to please the APIs of + * Mac OS X. In reality, NTFS has no backup time. + */ + + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +int ntfs_macfuse_setchgtime(const char *path, const struct timespec *tv) +{ + ntfs_inode *ni; + int res = 0; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + if (tv) { + ni->last_mft_change_time = tv->tv_sec; + ntfs_fuse_update_times(ni, 0); + } + + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + +static void *ntfs_init(struct fuse_conn_info *conn) +{ +#if defined(__APPLE__) || defined(__DARWIN__) + FUSE_ENABLE_XTIMES(conn); +#endif +#ifdef FUSE_CAP_DONT_MASK + /* request umask not to be enforced by fuse */ + conn->want |= FUSE_CAP_DONT_MASK; +#endif /* defined FUSE_CAP_DONT_MASK */ +#ifdef FUSE_CAP_BIG_WRITES + if (ctx->big_writes + && ((ctx->vol->nr_clusters << ctx->vol->cluster_size_bits) + >= SAFE_CAPACITY_FOR_BIG_WRITES)) + conn->want |= FUSE_CAP_BIG_WRITES; +#endif +#ifdef FUSE_CAP_IOCTL_DIR + conn->want |= FUSE_CAP_IOCTL_DIR; +#endif /* defined(FUSE_CAP_IOCTL_DIR) */ + return NULL; +} + +static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) +{ + int res = 0; + ntfs_inode *ni; + ntfs_attr *na; + char *path = NULL; + ntfschar *stream_name; + int stream_name_len; + BOOL withusermapping; + struct SECURITY_CONTEXT security; + + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); + if (stream_name_len < 0) + return stream_name_len; + memset(stbuf, 0, sizeof(struct stat)); + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) { + res = -errno; + goto exit; + } + withusermapping = ntfs_fuse_fill_security_context(&security); +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* + * make sure the parent directory is searchable + */ + if (withusermapping + && !ntfs_allowed_dir_access(&security,path, + (!strcmp(org_path,"/") ? ni : (ntfs_inode*)NULL), + ni, S_IEXEC)) { + res = -EACCES; + goto exit; + } +#endif + if (((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + || (ni->flags & FILE_ATTR_REPARSE_POINT)) + && !stream_name_len) { + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + char *target; + int attr_size; + + errno = 0; + target = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size); + /* + * If the reparse point is not a valid + * directory junction, and there is no error + * we still display as a symlink + */ + if (target || (errno == EOPNOTSUPP)) { + /* returning attribute size */ + if (target) + stbuf->st_size = attr_size; + else + stbuf->st_size = sizeof(ntfs_bad_reparse); + stbuf->st_blocks = (ni->allocated_size + 511) >> 9; + stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count); + stbuf->st_mode = S_IFLNK; + free(target); + } else { + res = -errno; + goto exit; + } + } else { + /* Directory. */ + stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask); + /* get index size, if not known */ + if (!test_nino_flag(ni, KnownSize)) { + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); + if (na) { + ni->data_size = na->data_size; + ni->allocated_size = na->allocated_size; + set_nino_flag(ni, KnownSize); + ntfs_attr_close(na); + } + } + stbuf->st_size = ni->data_size; + stbuf->st_blocks = ni->allocated_size >> 9; + stbuf->st_nlink = 1; /* Make find(1) work */ + } + } else { + /* Regular or Interix (INTX) file. */ + stbuf->st_mode = S_IFREG; + stbuf->st_size = ni->data_size; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + /* + * return data size rounded to next 512 byte boundary for + * encrypted files to include padding required for decryption + * also include 2 bytes for padding info + */ + if (ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED) + && ni->data_size) + stbuf->st_size = ((ni->data_size + 511) & ~511) + 2; +#endif /* HAVE_SETXATTR */ + /* + * Temporary fix to make ActiveSync work via Samba 3.0. + * See more on the ntfs-3g-devel list. + */ + stbuf->st_blocks = (ni->allocated_size + 511) >> 9; + stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count); + if (ni->flags & FILE_ATTR_SYSTEM || stream_name_len) { + na = ntfs_attr_open(ni, AT_DATA, stream_name, + stream_name_len); + if (!na) { + if (stream_name_len) { + res = -ENOENT; + goto exit; + } else + goto nodata; + } + if (stream_name_len) { + stbuf->st_size = na->data_size; + stbuf->st_blocks = na->allocated_size >> 9; + } + /* Check whether it's Interix FIFO or socket. */ + if (!(ni->flags & FILE_ATTR_HIDDEN) && + !stream_name_len) { + /* FIFO. */ + if (na->data_size == 0) + stbuf->st_mode = S_IFIFO; + /* Socket link. */ + if (na->data_size == 1) + stbuf->st_mode = S_IFSOCK; + } +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + /* encrypted named stream */ + /* round size up to next 512 byte boundary */ + if (ctx->efs_raw && stream_name_len && + (na->data_flags & ATTR_IS_ENCRYPTED) && + NAttrNonResident(na)) + stbuf->st_size = ((na->data_size+511) & ~511)+2; +#endif /* HAVE_SETXATTR */ + /* + * Check whether it's Interix symbolic link, block or + * character device. + */ + if ((u64)na->data_size <= sizeof(INTX_FILE_TYPES) + + sizeof(ntfschar) * PATH_MAX + && (u64)na->data_size > + sizeof(INTX_FILE_TYPES) + && !stream_name_len) { + + INTX_FILE *intx_file; + + intx_file = ntfs_malloc(na->data_size); + if (!intx_file) { + res = -errno; + ntfs_attr_close(na); + goto exit; + } + if (ntfs_attr_pread(na, 0, na->data_size, + intx_file) != na->data_size) { + res = -errno; + free(intx_file); + ntfs_attr_close(na); + goto exit; + } + if (intx_file->magic == INTX_BLOCK_DEVICE && + na->data_size == offsetof( + INTX_FILE, device_end)) { + stbuf->st_mode = S_IFBLK; + stbuf->st_rdev = makedev(le64_to_cpu( + intx_file->major), + le64_to_cpu( + intx_file->minor)); + } + if (intx_file->magic == INTX_CHARACTER_DEVICE && + na->data_size == offsetof( + INTX_FILE, device_end)) { + stbuf->st_mode = S_IFCHR; + stbuf->st_rdev = makedev(le64_to_cpu( + intx_file->major), + le64_to_cpu( + intx_file->minor)); + } + if (intx_file->magic == INTX_SYMBOLIC_LINK) + stbuf->st_mode = S_IFLNK; + free(intx_file); + } + ntfs_attr_close(na); + } + stbuf->st_mode |= (0777 & ~ctx->fmask); + } + if (withusermapping) { + if (ntfs_get_owner_mode(&security,ni,stbuf) < 0) + set_fuse_error(&res); + } else { + stbuf->st_uid = ctx->uid; + stbuf->st_gid = ctx->gid; + } + if (S_ISLNK(stbuf->st_mode)) + stbuf->st_mode |= 0777; +nodata : + stbuf->st_ino = ni->mft_no; +#ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC + stbuf->st_atimespec = ntfs2timespec(ni->last_access_time); + stbuf->st_ctimespec = ntfs2timespec(ni->last_mft_change_time); + stbuf->st_mtimespec = ntfs2timespec(ni->last_data_change_time); +#elif defined(HAVE_STRUCT_STAT_ST_ATIM) + stbuf->st_atim = ntfs2timespec(ni->last_access_time); + stbuf->st_ctim = ntfs2timespec(ni->last_mft_change_time); + stbuf->st_mtim = ntfs2timespec(ni->last_data_change_time); +#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC) + { + struct timespec ts; + + ts = ntfs2timespec(ni->last_access_time); + stbuf->st_atime = ts.tv_sec; + stbuf->st_atimensec = ts.tv_nsec; + ts = ntfs2timespec(ni->last_mft_change_time); + stbuf->st_ctime = ts.tv_sec; + stbuf->st_ctimensec = ts.tv_nsec; + ts = ntfs2timespec(ni->last_data_change_time); + stbuf->st_mtime = ts.tv_sec; + stbuf->st_mtimensec = ts.tv_nsec; + } +#else +#warning "No known way to set nanoseconds in struct stat !" + { + struct timespec ts; + + ts = ntfs2timespec(ni->last_access_time); + stbuf->st_atime = ts.tv_sec; + ts = ntfs2timespec(ni->last_mft_change_time); + stbuf->st_ctime = ts.tv_sec; + ts = ntfs2timespec(ni->last_data_change_time); + stbuf->st_mtime = ts.tv_sec; + } +#endif +exit: + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + free(path); + if (stream_name_len) + free(stream_name); + return res; +} + +static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size) +{ + char *path = NULL; + ntfschar *stream_name; + ntfs_inode *ni = NULL; + ntfs_attr *na = NULL; + INTX_FILE *intx_file = NULL; + int stream_name_len, res = 0; + + /* Get inode. */ + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); + if (stream_name_len < 0) + return stream_name_len; + if (stream_name_len > 0) { + res = -EINVAL; + goto exit; + } + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) { + res = -errno; + goto exit; + } + /* + * Reparse point : analyze as a junction point + */ + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + char *target; + int attr_size; + + errno = 0; + res = 0; + target = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size); + if (target) { + strncpy(buf,target,buf_size); + free(target); + } else + if (errno == EOPNOTSUPP) + strcpy(buf,ntfs_bad_reparse); + else + res = -errno; + goto exit; + } + /* Sanity checks. */ + if (!(ni->flags & FILE_ATTR_SYSTEM)) { + res = -EINVAL; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + res = -errno; + goto exit; + } + if ((size_t)na->data_size <= sizeof(INTX_FILE_TYPES)) { + res = -EINVAL; + goto exit; + } + if ((size_t)na->data_size > sizeof(INTX_FILE_TYPES) + + sizeof(ntfschar) * PATH_MAX) { + res = -ENAMETOOLONG; + goto exit; + } + /* Receive file content. */ + intx_file = ntfs_malloc(na->data_size); + if (!intx_file) { + res = -errno; + goto exit; + } + if (ntfs_attr_pread(na, 0, na->data_size, intx_file) != na->data_size) { + res = -errno; + goto exit; + } + /* Sanity check. */ + if (intx_file->magic != INTX_SYMBOLIC_LINK) { + res = -EINVAL; + goto exit; + } + /* Convert link from unicode to local encoding. */ + if (ntfs_ucstombs(intx_file->target, (na->data_size - + offsetof(INTX_FILE, target)) / sizeof(ntfschar), + &buf, buf_size) < 0) { + res = -errno; + goto exit; + } +exit: + if (intx_file) + free(intx_file); + if (na) + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + free(path); + if (stream_name_len) + free(stream_name); + return res; +} + +static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, + const ntfschar *name, const int name_len, const int name_type, + const s64 pos __attribute__((unused)), const MFT_REF mref, + const unsigned dt_type __attribute__((unused))) +{ + char *filename = NULL; + int ret = 0; + int filenamelen = -1; + + if (name_type == FILE_NAME_DOS) + return 0; + + if ((filenamelen = ntfs_ucstombs(name, name_len, &filename, 0)) < 0) { + ntfs_log_perror("Filename decoding failed (inode %llu)", + (unsigned long long)MREF(mref)); + return -1; + } + + if (ntfs_fuse_is_named_data_stream(filename)) { + ntfs_log_error("Unable to access '%s' (inode %llu) with " + "current named streams access interface.\n", + filename, (unsigned long long)MREF(mref)); + free(filename); + return 0; + } else { + struct stat st = { .st_ino = MREF(mref) }; + + switch (dt_type) { + case NTFS_DT_DIR : + st.st_mode = S_IFDIR | (0777 & ~ctx->dmask); + break; + case NTFS_DT_LNK : + st.st_mode = S_IFLNK | 0777; + break; + case NTFS_DT_FIFO : + st.st_mode = S_IFIFO; + break; + case NTFS_DT_SOCK : + st.st_mode = S_IFSOCK; + break; + case NTFS_DT_BLK : + st.st_mode = S_IFBLK; + break; + case NTFS_DT_CHR : + st.st_mode = S_IFCHR; + break; + default : /* unexpected types shown as plain files */ + case NTFS_DT_REG : + st.st_mode = S_IFREG | (0777 & ~ctx->fmask); + break; + } + +#if defined(__APPLE__) || defined(__DARWIN__) + /* + * Returning file names larger than MAXNAMLEN (255) bytes + * causes Darwin/Mac OS X to bug out and skip the entry. + */ + if (filenamelen > MAXNAMLEN) { + ntfs_log_debug("Truncating %d byte filename to " + "%d bytes.\n", filenamelen, MAXNAMLEN); + ntfs_log_debug(" before: '%s'\n", filename); + memset(filename + MAXNAMLEN, 0, filenamelen - MAXNAMLEN); + ntfs_log_debug(" after: '%s'\n", filename); + } +#elif defined(__sun) && defined (__SVR4) + /* + * Returning file names larger than MAXNAMELEN (256) bytes + * causes Solaris/Illumos to return an I/O error from the system + * call. + * However we also need space for a terminating NULL, or user + * space tools will bug out since they expect a NULL terminator. + * Effectively the maximum length of a file name is MAXNAMELEN - + * 1 (255). + */ + if (filenamelen > (MAXNAMELEN - 1)) { + ntfs_log_debug("Truncating %d byte filename to %d " + "bytes.\n", filenamelen, MAXNAMELEN - 1); + ntfs_log_debug(" before: '%s'\n", filename); + memset(&filename[MAXNAMELEN - 1], 0, + filenamelen - (MAXNAMELEN - 1)); + ntfs_log_debug(" after: '%s'\n", filename); + } +#endif /* defined(__APPLE__) || defined(__DARWIN__), ... */ + + ret = fill_ctx->filler(fill_ctx->buf, filename, &st, 0); + } + + free(filename); + return ret; +} + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + +static int ntfs_fuse_opendir(const char *path, + struct fuse_file_info *fi) +{ + int res = 0; + ntfs_inode *ni; + int accesstype; + struct SECURITY_CONTEXT security; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (ni) { + if (ntfs_fuse_fill_security_context(&security)) { + if (fi->flags & O_WRONLY) + accesstype = S_IWRITE; + else + if (fi->flags & O_RDWR) + accesstype = S_IWRITE | S_IREAD; + else + accesstype = S_IREAD; + /* + * directory must be searchable + * and requested access be allowed + */ + if (!strcmp(path,"/") + ? !ntfs_allowed_dir_access(&security, + path, ni, ni, accesstype | S_IEXEC) + : !ntfs_allowed_dir_access(&security, path, + (ntfs_inode*)NULL, ni, S_IEXEC) + || !ntfs_allowed_access(&security, + ni,accesstype)) + res = -EACCES; + } + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } else + res = -errno; + return res; +} + +#endif + +static int ntfs_fuse_readdir(const char *path, void *buf, + fuse_fill_dir_t filler, off_t offset __attribute__((unused)), + struct fuse_file_info *fi __attribute__((unused))) +{ + ntfs_fuse_fill_context_t fill_ctx; + ntfs_inode *ni; + s64 pos = 0; + int err = 0; + + fill_ctx.filler = filler; + fill_ctx.buf = buf; + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + if (ntfs_readdir(ni, &pos, &fill_ctx, + (ntfs_filldir_t)ntfs_fuse_filler)) + err = -errno; + ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME); + if (ntfs_inode_close(ni)) + set_fuse_error(&err); + return err; +} + +static int ntfs_fuse_open(const char *org_path, +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + struct fuse_file_info *fi) +#else + struct fuse_file_info *fi __attribute__((unused))) +#endif +{ + ntfs_inode *ni; + ntfs_attr *na; + int res = 0; + char *path = NULL; + ntfschar *stream_name; + int stream_name_len; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + int accesstype; + struct SECURITY_CONTEXT security; +#endif + + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); + if (stream_name_len < 0) + return stream_name_len; + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (ni) { + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (na) { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + if (ntfs_fuse_fill_security_context(&security)) { + if (fi->flags & O_WRONLY) + accesstype = S_IWRITE; + else + if (fi->flags & O_RDWR) + accesstype = S_IWRITE | S_IREAD; + else + accesstype = S_IREAD; + /* + * directory must be searchable + * and requested access allowed + */ + if (!ntfs_allowed_dir_access(&security, + path,(ntfs_inode*)NULL,ni,S_IEXEC) + || !ntfs_allowed_access(&security, + ni,accesstype)) + res = -EACCES; + } +#endif + if ((res >= 0) + && (fi->flags & (O_WRONLY | O_RDWR))) { + /* mark a future need to compress the last chunk */ + if (na->data_flags & ATTR_COMPRESSION_MASK) + fi->fh |= CLOSE_COMPRESSED; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + /* mark a future need to fixup encrypted inode */ + if (ctx->efs_raw + && !(na->data_flags & ATTR_IS_ENCRYPTED) + && (ni->flags & FILE_ATTR_ENCRYPTED)) + fi->fh |= CLOSE_ENCRYPTED; +#endif /* HAVE_SETXATTR */ + /* mark a future need to update the mtime */ + if (ctx->dmtime) + fi->fh |= CLOSE_DMTIME; + /* deny opening metadata files for writing */ + if (ni->mft_no < FILE_first_user) + res = -EPERM; + } + ntfs_attr_close(na); + } else + res = -errno; + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } else + res = -errno; + free(path); + if (stream_name_len) + free(stream_name); + return res; +} + +static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, + off_t offset, struct fuse_file_info *fi __attribute__((unused))) +{ + ntfs_inode *ni = NULL; + ntfs_attr *na = NULL; + char *path = NULL; + ntfschar *stream_name; + int stream_name_len, res; + s64 total = 0; + s64 max_read; + + if (!size) + return 0; + + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); + if (stream_name_len < 0) + return stream_name_len; + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) { + res = -errno; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + max_read = na->data_size; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + /* limit reads at next 512 byte boundary for encrypted attributes */ + if (ctx->efs_raw + && max_read + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) { + max_read = ((na->data_size+511) & ~511) + 2; + } +#endif /* HAVE_SETXATTR */ + if (offset + (off_t)size > max_read) { + if (max_read < offset) + goto ok; + size = max_read - offset; + } + while (size > 0) { + s64 ret = ntfs_attr_pread(na, offset, size, buf + total); + if (ret != (s64)size) + ntfs_log_perror("ntfs_attr_pread error reading '%s' at " + "offset %lld: %lld <> %lld", org_path, + (long long)offset, (long long)size, (long long)ret); + if (ret <= 0 || ret > (s64)size) { + res = (ret < 0) ? -errno : -EIO; + goto exit; + } + size -= ret; + offset += ret; + total += ret; + } +ok: + ntfs_fuse_update_times(na->ni, NTFS_UPDATE_ATIME); + res = total; +exit: + if (na) + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + free(path); + if (stream_name_len) + free(stream_name); + return res; +} + +static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi __attribute__((unused))) +{ + ntfs_inode *ni = NULL; + ntfs_attr *na = NULL; + char *path = NULL; + ntfschar *stream_name; + int stream_name_len, res, total = 0; + + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); + if (stream_name_len < 0) { + res = stream_name_len; + goto out; + } + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) { + res = -errno; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + while (size) { + s64 ret = ntfs_attr_pwrite(na, offset, size, buf + total); + if (ret <= 0) { + res = -errno; + goto exit; + } + size -= ret; + offset += ret; + total += ret; + } + res = total; + if ((res > 0) + && (!ctx->dmtime + || (le64_to_cpu(ntfs_current_time()) + - le64_to_cpu(ni->last_data_change_time)) > ctx->dmtime)) + ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME); +exit: + if (na) + ntfs_attr_close(na); + if (total) + set_archive(ni); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + free(path); + if (stream_name_len) + free(stream_name); +out: + return res; +} + +static int ntfs_fuse_release(const char *org_path, + struct fuse_file_info *fi) +{ + ntfs_inode *ni = NULL; + ntfs_attr *na = NULL; + char *path = NULL; + ntfschar *stream_name; + int stream_name_len, res; + + /* Only for marked descriptors there is something to do */ + if (!(fi->fh & (CLOSE_COMPRESSED | CLOSE_ENCRYPTED | CLOSE_DMTIME))) { + res = 0; + goto out; + } + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); + if (stream_name_len < 0) { + res = stream_name_len; + goto out; + } + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) { + res = -errno; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) { + res = -errno; + goto exit; + } + res = 0; + if (fi->fh & CLOSE_DMTIME) + ntfs_inode_update_times(na->ni,NTFS_UPDATE_MCTIME); + if (fi->fh & CLOSE_COMPRESSED) + res = ntfs_attr_pclose(na); +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + if (fi->fh & CLOSE_ENCRYPTED) + res = ntfs_efs_fixup_attribute(NULL, na); +#endif /* HAVE_SETXATTR */ +exit: + if (na) + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + free(path); + if (stream_name_len) + free(stream_name); +out: + return res; +} + +/* + * Common part for truncate() and ftruncate() + */ + +static int ntfs_fuse_trunc(const char *org_path, off_t size, +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + BOOL chkwrite) +#else + BOOL chkwrite __attribute__((unused))) +#endif +{ + ntfs_inode *ni = NULL; + ntfs_attr *na = NULL; + int res; + char *path = NULL; + ntfschar *stream_name; + int stream_name_len; + s64 oldsize; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + struct SECURITY_CONTEXT security; +#endif + + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); + if (stream_name_len < 0) + return stream_name_len; + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + goto exit; + /* deny truncating metadata files */ + if (ni->mft_no < FILE_first_user) { + errno = EPERM; + goto exit; + } + + na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); + if (!na) + goto exit; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* + * JPA deny truncation if cannot search in parent directory + * or cannot write to file (already checked for ftruncate()) + */ + if (ntfs_fuse_fill_security_context(&security) + && (!ntfs_allowed_dir_access(&security, path, + (ntfs_inode*)NULL, ni, S_IEXEC) + || (chkwrite + && !ntfs_allowed_access(&security, ni, S_IWRITE)))) { + errno = EACCES; + goto exit; + } +#endif + /* + * For compressed files, upsizing is done by inserting a final + * zero, which is optimized as creating a hole when possible. + */ + oldsize = na->data_size; + if ((na->data_flags & ATTR_COMPRESSION_MASK) + && (size > na->initialized_size)) { + char zero = 0; + if (ntfs_attr_pwrite(na, size - 1, 1, &zero) <= 0) + goto exit; + } else + if (ntfs_attr_truncate(na, size)) + goto exit; + if (oldsize != size) + set_archive(ni); + + ntfs_fuse_update_times(na->ni, NTFS_UPDATE_MCTIME); + errno = 0; +exit: + res = -errno; + ntfs_attr_close(na); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + free(path); + if (stream_name_len) + free(stream_name); + return res; +} + +static int ntfs_fuse_truncate(const char *org_path, off_t size) +{ + return ntfs_fuse_trunc(org_path, size, TRUE); +} + +static int ntfs_fuse_ftruncate(const char *org_path, off_t size, + struct fuse_file_info *fi __attribute__((unused))) +{ + /* + * in ->ftruncate() the file handle is guaranteed + * to have been opened for write. + */ + return (ntfs_fuse_trunc(org_path, size, FALSE)); +} + +static int ntfs_fuse_chmod(const char *path, + mode_t mode) +{ + int res = 0; + ntfs_inode *ni; + struct SECURITY_CONTEXT security; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + + /* + * Return unsupported if no user mapping has been defined + * or enforcing Windows-type inheritance + */ + if (ctx->inherit + || !ntfs_fuse_fill_security_context(&security)) { + if (ctx->silent) + res = 0; + else + res = -EOPNOTSUPP; + } else { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* parent directory must be executable */ + if (ntfs_allowed_dir_access(&security,path, + (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) { +#endif + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + res = -errno; + else { + if (ntfs_set_mode(&security,ni,mode)) + res = -errno; + else + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + NInoSetDirty(ni); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + } else + res = -errno; +#endif + } + return res; +} + +static int ntfs_fuse_chown(const char *path, uid_t uid, gid_t gid) +{ + ntfs_inode *ni; + int res; + struct SECURITY_CONTEXT security; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + /* + * Return unsupported if no user mapping has been defined + * or enforcing Windows-type inheritance + */ + if (ctx->inherit + || !ntfs_fuse_fill_security_context(&security)) { + if (ctx->silent) + return 0; + if (uid == ctx->uid && gid == ctx->gid) + return 0; + return -EOPNOTSUPP; + } else { + res = 0; + if (((int)uid != -1) || ((int)gid != -1)) { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* parent directory must be executable */ + + if (ntfs_allowed_dir_access(&security,path, + (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) { +#endif + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + res = -errno; + else { + if (ntfs_set_owner(&security, + ni,uid,gid)) + res = -errno; + else + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + } else + res = -errno; +#endif + } + } + return (res); +} + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + +static int ntfs_fuse_access(const char *path, int type) +{ + int res = 0; + int mode; + ntfs_inode *ni; + struct SECURITY_CONTEXT security; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + + /* JPA return unsupported if no user mapping has been defined */ + if (!ntfs_fuse_fill_security_context(&security)) { + if (ctx->silent) + res = 0; + else + res = -EOPNOTSUPP; + } else { + /* parent directory must be seachable */ + if (ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, + (ntfs_inode*)NULL,S_IEXEC)) { + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) { + res = -errno; + } else { + mode = 0; + if (type & (X_OK | W_OK | R_OK)) { + if (type & X_OK) mode += S_IEXEC; + if (type & W_OK) mode += S_IWRITE; + if (type & R_OK) mode += S_IREAD; + if (!ntfs_allowed_access(&security, + ni, mode)) + res = -errno; + } + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } else + res = -errno; + } + return (res); +} + +#endif + +static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev, + const char *target, struct fuse_file_info *fi) +{ + char *name; + ntfschar *uname = NULL, *utarget = NULL; + ntfs_inode *dir_ni = NULL, *ni; + char *dir_path; + le32 securid; + char *path = NULL; + gid_t gid; + mode_t dsetgid; + ntfschar *stream_name; + int stream_name_len; + mode_t type = typemode & ~07777; + mode_t perm; + struct SECURITY_CONTEXT security; + int res = 0, uname_len, utarget_len; + + dir_path = strdup(org_path); + if (!dir_path) + return -errno; + /* Generate unicode filename. */ + name = strrchr(dir_path, '/'); + name++; + uname_len = ntfs_mbstoucs(name, &uname); + if ((uname_len < 0) + || (ctx->windows_names + && ntfs_forbidden_names(ctx->vol,uname,uname_len))) { + res = -errno; + goto exit; + } + stream_name_len = ntfs_fuse_parse_path(org_path, + &path, &stream_name); + /* stream name validity has been checked previously */ + if (stream_name_len < 0) { + res = stream_name_len; + goto exit; + } + /* Open parent directory. */ + *--name = 0; + dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, dir_path); + if (!dir_ni) { + free(path); + res = -errno; + goto exit; + } +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* make sure parent directory is writeable and executable */ + if (!ntfs_fuse_fill_security_context(&security) + || ntfs_allowed_create(&security, + dir_ni, &gid, &dsetgid)) { +#else + ntfs_fuse_fill_security_context(&security); + ntfs_allowed_create(&security, dir_ni, &gid, &dsetgid); +#endif + if (S_ISDIR(type)) + perm = (typemode & ~ctx->dmask & 0777) + | (dsetgid & S_ISGID); + else + perm = typemode & ~ctx->fmask & 0777; + /* + * Try to get a security id available for + * file creation (from inheritance or argument). + * This is not possible for NTFS 1.x, and we will + * have to build a security attribute later. + */ + if (!ctx->security.mapping[MAPUSERS]) + securid = 0; + else + if (ctx->inherit) + securid = ntfs_inherited_id(&security, + dir_ni, S_ISDIR(type)); + else +#if POSIXACLS + securid = ntfs_alloc_securid(&security, + security.uid, gid, + dir_ni, perm, S_ISDIR(type)); +#else + securid = ntfs_alloc_securid(&security, + security.uid, gid, + perm & ~security.umask, S_ISDIR(type)); +#endif + /* Create object specified in @type. */ + switch (type) { + case S_IFCHR: + case S_IFBLK: + ni = ntfs_create_device(dir_ni, securid, + uname, uname_len, type, dev); + break; + case S_IFLNK: + utarget_len = ntfs_mbstoucs(target, &utarget); + if (utarget_len < 0) { + res = -errno; + goto exit; + } + ni = ntfs_create_symlink(dir_ni, securid, + uname, uname_len, + utarget, utarget_len); + break; + default: + ni = ntfs_create(dir_ni, securid, uname, + uname_len, type); + break; + } + if (ni) { + /* + * set the security attribute if a security id + * could not be allocated (eg NTFS 1.x) + */ + if (ctx->security.mapping[MAPUSERS]) { +#if POSIXACLS + if (!securid + && ntfs_set_inherited_posix(&security, ni, + security.uid, gid, + dir_ni, perm) < 0) + set_fuse_error(&res); +#else + if (!securid + && ntfs_set_owner_mode(&security, ni, + security.uid, gid, + perm & ~security.umask) < 0) + set_fuse_error(&res); +#endif + } + set_archive(ni); + /* mark a need to compress the end of file */ + if (fi && (ni->flags & FILE_ATTR_COMPRESSED)) { + fi->fh |= CLOSE_COMPRESSED; + } +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + /* mark a future need to fixup encrypted inode */ + if (fi + && ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED)) + fi->fh |= CLOSE_ENCRYPTED; +#endif /* HAVE_SETXATTR */ + /* mark a need to update the mtime */ + if (fi && ctx->dmtime) + fi->fh |= CLOSE_DMTIME; + NInoSetDirty(ni); + /* + * closing ni requires access to dir_ni to + * synchronize the index, avoid double opening. + */ + if (ntfs_inode_close_in_dir(ni, dir_ni)) + set_fuse_error(&res); + ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME); + } else + res = -errno; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + } else + res = -errno; +#endif + free(path); + +exit: + free(uname); + if (ntfs_inode_close(dir_ni)) + set_fuse_error(&res); + if (utarget) + free(utarget); + free(dir_path); + return res; +} + +static int ntfs_fuse_create_stream(const char *path, + ntfschar *stream_name, const int stream_name_len, + struct fuse_file_info *fi) +{ + ntfs_inode *ni; + int res = 0; + + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) { + res = -errno; + if (res == -ENOENT) { + /* + * If such file does not exist, create it and try once + * again to add stream to it. + * Note : no fuse_file_info for creation of main file + */ + res = ntfs_fuse_create(path, S_IFREG, 0, NULL, + (struct fuse_file_info*)NULL); + if (!res) + return ntfs_fuse_create_stream(path, + stream_name, stream_name_len,fi); + else + res = -errno; + } + return res; + } + if (ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_len, NULL, 0)) + res = -errno; + else + set_archive(ni); + + if ((res >= 0) + && fi + && (fi->flags & (O_WRONLY | O_RDWR))) { + /* mark a future need to compress the last block */ + if (ni->flags & FILE_ATTR_COMPRESSED) + fi->fh |= CLOSE_COMPRESSED; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + /* mark a future need to fixup encrypted inode */ + if (ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED)) + fi->fh |= CLOSE_ENCRYPTED; +#endif /* HAVE_SETXATTR */ + if (ctx->dmtime) + fi->fh |= CLOSE_DMTIME; + } + + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +static int ntfs_fuse_mknod_common(const char *org_path, mode_t mode, dev_t dev, + struct fuse_file_info *fi) +{ + char *path = NULL; + ntfschar *stream_name; + int stream_name_len; + int res = 0; + + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); + if (stream_name_len < 0) + return stream_name_len; + if (stream_name_len + && (!S_ISREG(mode) + || (ctx->windows_names + && ntfs_forbidden_names(ctx->vol,stream_name, + stream_name_len)))) { + res = -EINVAL; + goto exit; + } + if (!stream_name_len) + res = ntfs_fuse_create(path, mode & (S_IFMT | 07777), dev, + NULL,fi); + else + res = ntfs_fuse_create_stream(path, stream_name, + stream_name_len,fi); +exit: + free(path); + if (stream_name_len) + free(stream_name); + return res; +} + +static int ntfs_fuse_mknod(const char *path, mode_t mode, dev_t dev) +{ + return ntfs_fuse_mknod_common(path, mode, dev, + (struct fuse_file_info*)NULL); +} + +static int ntfs_fuse_create_file(const char *path, mode_t mode, + struct fuse_file_info *fi) +{ + return ntfs_fuse_mknod_common(path, mode, 0, fi); +} + +static int ntfs_fuse_symlink(const char *to, const char *from) +{ + if (ntfs_fuse_is_named_data_stream(from)) + return -EINVAL; /* n/a for named data streams. */ + return ntfs_fuse_create(from, S_IFLNK, 0, to, + (struct fuse_file_info*)NULL); +} + +static int ntfs_fuse_link(const char *old_path, const char *new_path) +{ + char *name; + ntfschar *uname = NULL; + ntfs_inode *dir_ni = NULL, *ni; + char *path; + int res = 0, uname_len; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + BOOL samedir; + struct SECURITY_CONTEXT security; +#endif + + if (ntfs_fuse_is_named_data_stream(old_path)) + return -EINVAL; /* n/a for named data streams. */ + if (ntfs_fuse_is_named_data_stream(new_path)) + return -EINVAL; /* n/a for named data streams. */ + path = strdup(new_path); + if (!path) + return -errno; + /* Open file for which create hard link. */ + ni = ntfs_pathname_to_inode(ctx->vol, NULL, old_path); + if (!ni) { + res = -errno; + goto exit; + } + + /* Generate unicode filename. */ + name = strrchr(path, '/'); + name++; + uname_len = ntfs_mbstoucs(name, &uname); + if ((uname_len < 0) + || (ctx->windows_names + && ntfs_forbidden_names(ctx->vol,uname,uname_len))) { + res = -errno; + goto exit; + } + /* Open parent directory. */ + *--name = 0; + dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!dir_ni) { + res = -errno; + goto exit; + } + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + samedir = !strncmp(old_path, path, strlen(path)) + && (old_path[strlen(path)] == '/'); + /* JPA make sure the parent directories are writeable */ + if (ntfs_fuse_fill_security_context(&security) + && ((!samedir && !ntfs_allowed_dir_access(&security,old_path, + (ntfs_inode*)NULL,ni,S_IWRITE + S_IEXEC)) + || !ntfs_allowed_access(&security,dir_ni,S_IWRITE + S_IEXEC))) + res = -EACCES; + else +#endif + { + if (ntfs_link(ni, dir_ni, uname, uname_len)) { + res = -errno; + goto exit; + } + + set_archive(ni); + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_MCTIME); + } +exit: + /* + * Must close dir_ni first otherwise ntfs_inode_sync_file_name(ni) + * may fail because ni may not be in parent's index on the disk yet. + */ + if (ntfs_inode_close(dir_ni)) + set_fuse_error(&res); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + free(uname); + free(path); + return res; +} + +static int ntfs_fuse_rm(const char *org_path) +{ + char *name; + ntfschar *uname = NULL; + ntfs_inode *dir_ni = NULL, *ni; + char *path; + int res = 0, uname_len; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + struct SECURITY_CONTEXT security; +#endif + + path = strdup(org_path); + if (!path) + return -errno; + /* Open object for delete. */ + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) { + res = -errno; + goto exit; + } + /* deny unlinking metadata files */ + if (ni->mft_no < FILE_first_user) { + errno = EPERM; + res = -errno; + goto exit; + } + + /* Generate unicode filename. */ + name = strrchr(path, '/'); + name++; + uname_len = ntfs_mbstoucs(name, &uname); + if (uname_len < 0) { + res = -errno; + goto exit; + } + /* Open parent directory. */ + *--name = 0; + dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!dir_ni) { + res = -errno; + goto exit; + } + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* JPA deny unlinking if directory is not writable and executable */ + if (!ntfs_fuse_fill_security_context(&security) + || ntfs_allowed_dir_access(&security, org_path, dir_ni, ni, + S_IEXEC + S_IWRITE + S_ISVTX)) { +#endif + if (ntfs_delete(ctx->vol, org_path, ni, dir_ni, + uname, uname_len)) + res = -errno; + /* ntfs_delete() always closes ni and dir_ni */ + ni = dir_ni = NULL; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + } else + res = -EACCES; +#endif +exit: + if (ntfs_inode_close(dir_ni)) + set_fuse_error(&res); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + free(uname); + free(path); + return res; +} + +static int ntfs_fuse_rm_stream(const char *path, ntfschar *stream_name, + const int stream_name_len) +{ + ntfs_inode *ni; + int res = 0; + + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + if (ntfs_attr_remove(ni, AT_DATA, stream_name, stream_name_len)) + res = -errno; + + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +static int ntfs_fuse_unlink(const char *org_path) +{ + char *path = NULL; + ntfschar *stream_name; + int stream_name_len; + int res = 0; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + struct SECURITY_CONTEXT security; +#endif + + stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); + if (stream_name_len < 0) + return stream_name_len; + if (!stream_name_len) + res = ntfs_fuse_rm(path); + else { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* + * JPA deny unlinking stream if directory is not + * writable and executable (debatable) + */ + if (!ntfs_fuse_fill_security_context(&security) + || ntfs_allowed_dir_access(&security, path, + (ntfs_inode*)NULL, (ntfs_inode*)NULL, + S_IEXEC + S_IWRITE + S_ISVTX)) + res = ntfs_fuse_rm_stream(path, stream_name, + stream_name_len); + else + res = -errno; +#else + res = ntfs_fuse_rm_stream(path, stream_name, stream_name_len); +#endif + } + free(path); + if (stream_name_len) + free(stream_name); + return res; +} + +static int ntfs_fuse_safe_rename(const char *old_path, + const char *new_path, + const char *tmp) +{ + int ret; + + ntfs_log_trace("Entering\n"); + + ret = ntfs_fuse_link(new_path, tmp); + if (ret) + return ret; + + ret = ntfs_fuse_unlink(new_path); + if (!ret) { + + ret = ntfs_fuse_link(old_path, new_path); + if (ret) + goto restore; + + ret = ntfs_fuse_unlink(old_path); + if (ret) { + if (ntfs_fuse_unlink(new_path)) + goto err; + goto restore; + } + } + + goto cleanup; +restore: + if (ntfs_fuse_link(tmp, new_path)) { +err: + ntfs_log_perror("Rename failed. Existing file '%s' was renamed " + "to '%s'", new_path, tmp); + } else { +cleanup: + /* + * Condition for this unlink has already been checked in + * "ntfs_fuse_rename_existing_dest()", so it should never + * fail (unless concurrent access to directories when fuse + * is multithreaded) + */ + if (ntfs_fuse_unlink(tmp) < 0) + ntfs_log_perror("Rename failed. Existing file '%s' still present " + "as '%s'", new_path, tmp); + } + return ret; +} + +static int ntfs_fuse_rename_existing_dest(const char *old_path, const char *new_path) +{ + int ret, len; + char *tmp; + const char *ext = ".ntfs-3g-"; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + struct SECURITY_CONTEXT security; +#endif + + ntfs_log_trace("Entering\n"); + + len = strlen(new_path) + strlen(ext) + 10 + 1; /* wc(str(2^32)) + \0 */ + tmp = ntfs_malloc(len); + if (!tmp) + return -errno; + + ret = snprintf(tmp, len, "%s%s%010d", new_path, ext, ++ntfs_sequence); + if (ret != len - 1) { + ntfs_log_error("snprintf failed: %d != %d\n", ret, len - 1); + ret = -EOVERFLOW; + } else { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* + * Make sure existing dest can be removed. + * This is only needed if parent directory is + * sticky, because in this situation condition + * for unlinking is different from condition for + * linking + */ + if (!ntfs_fuse_fill_security_context(&security) + || ntfs_allowed_dir_access(&security, new_path, + (ntfs_inode*)NULL, (ntfs_inode*)NULL, + S_IEXEC + S_IWRITE + S_ISVTX)) + ret = ntfs_fuse_safe_rename(old_path, new_path, tmp); + else + ret = -EACCES; +#else + ret = ntfs_fuse_safe_rename(old_path, new_path, tmp); +#endif + } + free(tmp); + return ret; +} + +static int ntfs_fuse_rename(const char *old_path, const char *new_path) +{ + int ret, stream_name_len; + char *path = NULL; + ntfschar *stream_name; + ntfs_inode *ni; + u64 inum; + BOOL same; + + ntfs_log_debug("rename: old: '%s' new: '%s'\n", old_path, new_path); + + /* + * FIXME: Rename should be atomic. + */ + stream_name_len = ntfs_fuse_parse_path(new_path, &path, &stream_name); + if (stream_name_len < 0) + return stream_name_len; + + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (ni) { + ret = ntfs_check_empty_dir(ni); + if (ret < 0) { + ret = -errno; + ntfs_inode_close(ni); + goto out; + } + + inum = ni->mft_no; + if (ntfs_inode_close(ni)) { + set_fuse_error(&ret); + goto out; + } + + free(path); + path = (char*)NULL; + if (stream_name_len) + free(stream_name); + + /* silently ignore a rename to same inode */ + stream_name_len = ntfs_fuse_parse_path(old_path, + &path, &stream_name); + if (stream_name_len < 0) + return stream_name_len; + + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (ni) { + same = ni->mft_no == inum; + if (ntfs_inode_close(ni)) + ret = -errno; + else + if (!same) + ret = ntfs_fuse_rename_existing_dest( + old_path, new_path); + } else + ret = -errno; + goto out; + } + + ret = ntfs_fuse_link(old_path, new_path); + if (ret) + goto out; + + ret = ntfs_fuse_unlink(old_path); + if (ret) + ntfs_fuse_unlink(new_path); +out: + free(path); + if (stream_name_len) + free(stream_name); + return ret; +} + +static int ntfs_fuse_mkdir(const char *path, + mode_t mode) +{ + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + return ntfs_fuse_create(path, S_IFDIR | (mode & 07777), 0, NULL, + (struct fuse_file_info*)NULL); +} + +static int ntfs_fuse_rmdir(const char *path) +{ + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ + return ntfs_fuse_rm(path); +} + +#ifdef HAVE_UTIMENSAT + +static int ntfs_fuse_utimens(const char *path, const struct timespec tv[2]) +{ + ntfs_inode *ni; + int res = 0; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + struct SECURITY_CONTEXT security; +#endif + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* parent directory must be executable */ + if (ntfs_fuse_fill_security_context(&security) + && !ntfs_allowed_dir_access(&security,path, + (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) { + return (-errno); + } +#endif + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + /* no check or update if both UTIME_OMIT */ + if ((tv[0].tv_nsec != UTIME_OMIT) || (tv[1].tv_nsec != UTIME_OMIT)) { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + if (ntfs_allowed_as_owner(&security, ni) + || ((tv[0].tv_nsec == UTIME_NOW) + && (tv[1].tv_nsec == UTIME_NOW) + && ntfs_allowed_access(&security, ni, S_IWRITE))) { +#endif + ntfs_time_update_flags mask = NTFS_UPDATE_CTIME; + + if (tv[0].tv_nsec == UTIME_NOW) + mask |= NTFS_UPDATE_ATIME; + else + if (tv[0].tv_nsec != UTIME_OMIT) + ni->last_access_time + = timespec2ntfs(tv[0]); + if (tv[1].tv_nsec == UTIME_NOW) + mask |= NTFS_UPDATE_MTIME; + else + if (tv[1].tv_nsec != UTIME_OMIT) + ni->last_data_change_time + = timespec2ntfs(tv[1]); + ntfs_inode_update_times(ni, mask); +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + } else + res = -errno; +#endif + } + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +#else /* HAVE_UTIMENSAT */ + +static int ntfs_fuse_utime(const char *path, struct utimbuf *buf) +{ + ntfs_inode *ni; + int res = 0; + struct timespec actime; + struct timespec modtime; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + BOOL ownerok; + BOOL writeok; + struct SECURITY_CONTEXT security; +#endif + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; /* n/a for named data streams. */ +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* parent directory must be executable */ + if (ntfs_fuse_fill_security_context(&security) + && !ntfs_allowed_dir_access(&security,path, + (ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) { + return (-errno); + } +#endif + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + ownerok = ntfs_allowed_as_owner(&security, ni); + if (buf) { + /* + * fuse never calls with a NULL buf and we do not + * know whether the specific condition can be applied + * So we have to accept updating by a non-owner having + * write access. + */ + writeok = !ownerok + && (buf->actime == buf->modtime) + && ntfs_allowed_access(&security, ni, S_IWRITE); + /* Must be owner */ + if (!ownerok && !writeok) + res = (buf->actime == buf->modtime ? -EACCES : -EPERM); + else { + actime.tv_sec = buf->actime; + actime.tv_nsec = 0; + modtime.tv_sec = buf->modtime; + modtime.tv_nsec = 0; + ni->last_access_time = timespec2ntfs(actime); + ni->last_data_change_time = timespec2ntfs(modtime); + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + } + } else { + /* Must be owner or have write access */ + writeok = !ownerok + && ntfs_allowed_access(&security, ni, S_IWRITE); + if (!ownerok && !writeok) + res = -EACCES; + else + ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME); + } +#else + if (buf) { + actime.tv_sec = buf->actime; + actime.tv_nsec = 0; + modtime.tv_sec = buf->modtime; + modtime.tv_nsec = 0; + ni->last_access_time = timespec2ntfs(actime); + ni->last_data_change_time = timespec2ntfs(modtime); + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + } else + ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME); +#endif + + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +#endif /* HAVE_UTIMENSAT */ + +static int ntfs_fuse_fsync(const char *path __attribute__((unused)), + int type __attribute__((unused)), + struct fuse_file_info *fi __attribute__((unused))) +{ + int ret; + + /* sync the full device */ + ret = ntfs_device_sync(ctx->vol->dev); + if (ret) + ret = -errno; + return (ret); +} + +#if defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28) +static int ntfs_fuse_ioctl(const char *path, + int cmd, void *arg, + struct fuse_file_info *fi __attribute__((unused)), + unsigned int flags, void *data) +{ + ntfs_inode *ni; + int ret; + + if (flags & FUSE_IOCTL_COMPAT) + return -ENOSYS; + + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + ret = ntfs_ioctl(ni, cmd, arg, flags, data); + + if (ntfs_inode_close (ni)) + set_fuse_error(&ret); + return ret; +} +#endif /* defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28) */ + +static int ntfs_fuse_bmap(const char *path, size_t blocksize, uint64_t *idx) +{ + ntfs_inode *ni; + ntfs_attr *na; + LCN lcn; + int ret = 0; + int cl_per_bl = ctx->vol->cluster_size / blocksize; + + if (blocksize > ctx->vol->cluster_size) + return -EINVAL; + + if (ntfs_fuse_is_named_data_stream(path)) + return -EINVAL; + + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + ret = -errno; + goto close_inode; + } + + if ((na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED)) + || !NAttrNonResident(na)) { + ret = -EINVAL; + goto close_attr; + } + + if (ntfs_attr_map_whole_runlist(na)) { + ret = -errno; + goto close_attr; + } + + lcn = ntfs_rl_vcn_to_lcn(na->rl, *idx / cl_per_bl); + *idx = (lcn > 0) ? lcn * cl_per_bl + *idx % cl_per_bl : 0; + +close_attr: + ntfs_attr_close(na); +close_inode: + if (ntfs_inode_close(ni)) + set_fuse_error(&ret); + return ret; +} + +#ifdef HAVE_SETXATTR + +/* + * Name space identifications and prefixes + */ + +enum { + XATTRNS_NONE, + XATTRNS_USER, + XATTRNS_SYSTEM, + XATTRNS_SECURITY, + XATTRNS_TRUSTED, + XATTRNS_OPEN +} ; + +/* + * Check whether access to internal data as an extended + * attribute in system name space is allowed + * + * Returns pointer to inode if allowed, + * NULL and errno set if not allowed + */ + +static ntfs_inode *ntfs_check_access_xattr(struct SECURITY_CONTEXT *security, + const char *path, int attr, BOOL setting) +{ + ntfs_inode *ni; + BOOL foracl; + mode_t acctype; + + ni = (ntfs_inode*)NULL; + if (ntfs_fuse_is_named_data_stream(path)) + errno = EINVAL; /* n/a for named data streams. */ + else { + foracl = (attr == XATTR_POSIX_ACC) + || (attr == XATTR_POSIX_DEF); + /* + * When accessing Posix ACL, return unsupported if ACL + * were disabled or no user mapping has been defined, + * or trying to change a Windows-inherited ACL. + * However no error will be returned to getfacl + */ + if (((!ntfs_fuse_fill_security_context(security) + || (ctx->secure_flags + & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_RAW)))) + || !(ctx->secure_flags & (1 << SECURITY_ACL)) + || (setting && ctx->inherit)) + && foracl) { + if (ctx->silent) + errno = 0; + else + errno = EOPNOTSUPP; + } else { + /* + * parent directory must be executable, and + * for setting a DOS name it must be writeable + */ + if (setting && (attr == XATTR_NTFS_DOS_NAME)) + acctype = S_IEXEC | S_IWRITE; + else + acctype = S_IEXEC; + if ((attr == XATTR_NTFS_DOS_NAME) + && !strcmp(path,"/")) + /* forbid getting/setting names on root */ + errno = EPERM; + else + if (ntfs_allowed_real_dir_access(security, path, + (ntfs_inode*)NULL ,acctype)) { + ni = ntfs_pathname_to_inode(ctx->vol, + NULL, path); + } + } + } + return (ni); +} + +/* + * Determine the name space of an extended attribute + */ + +static int xattr_namespace(const char *name) +{ + int namespace; + + if (ctx->streams == NF_STREAMS_INTERFACE_XATTR) { + namespace = XATTRNS_NONE; + if (!strncmp(name, nf_ns_user_prefix, + nf_ns_user_prefix_len) + && (strlen(name) != (size_t)nf_ns_user_prefix_len)) + namespace = XATTRNS_USER; + else if (!strncmp(name, nf_ns_system_prefix, + nf_ns_system_prefix_len) + && (strlen(name) != (size_t)nf_ns_system_prefix_len)) + namespace = XATTRNS_SYSTEM; + else if (!strncmp(name, nf_ns_security_prefix, + nf_ns_security_prefix_len) + && (strlen(name) != (size_t)nf_ns_security_prefix_len)) + namespace = XATTRNS_SECURITY; + else if (!strncmp(name, nf_ns_trusted_prefix, + nf_ns_trusted_prefix_len) + && (strlen(name) != (size_t)nf_ns_trusted_prefix_len)) + namespace = XATTRNS_TRUSTED; + } else + namespace = XATTRNS_OPEN; + return (namespace); +} + +/* + * Fix the prefix of an extended attribute + */ + +static int fix_xattr_prefix(const char *name, int namespace, ntfschar **lename) +{ + int len; + char *prefixed; + + *lename = (ntfschar*)NULL; + switch (namespace) { + case XATTRNS_USER : + /* + * user name space : remove user prefix + */ + len = ntfs_mbstoucs(name + nf_ns_user_prefix_len, lename); + break; + case XATTRNS_SYSTEM : + case XATTRNS_SECURITY : + case XATTRNS_TRUSTED : + /* + * security, trusted and unmapped system name spaces : + * insert ntfs-3g prefix + */ + prefixed = ntfs_malloc(strlen(xattr_ntfs_3g) + + strlen(name) + 1); + if (prefixed) { + strcpy(prefixed,xattr_ntfs_3g); + strcat(prefixed,name); + len = ntfs_mbstoucs(prefixed, lename); + free(prefixed); + } else + len = -1; + break; + case XATTRNS_OPEN : + /* + * in open name space mode : do no fix prefix + */ + len = ntfs_mbstoucs(name, lename); + break; + default : + len = -1; + } + return (len); +} + +static int ntfs_fuse_listxattr(const char *path, char *list, size_t size) +{ + ntfs_attr_search_ctx *actx = NULL; + ntfs_inode *ni; + int ret = 0; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + struct SECURITY_CONTEXT security; +#endif +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* parent directory must be executable */ + if (ntfs_fuse_fill_security_context(&security) + && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, + (ntfs_inode*)NULL,S_IEXEC)) { + return (-errno); + } +#endif + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + /* Return with no result for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) + goto exit; + /* otherwise file must be readable */ +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + if (!ntfs_allowed_access(&security,ni,S_IREAD)) { + ret = -EACCES; + goto exit; + } +#endif + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (!actx) { + ret = -errno; + goto exit; + } + + if ((ctx->streams == NF_STREAMS_INTERFACE_XATTR) + || (ctx->streams == NF_STREAMS_INTERFACE_OPENXATTR)) { + ret = ntfs_fuse_listxattr_common(ni, actx, list, size, + ctx->streams == NF_STREAMS_INTERFACE_XATTR); + if (ret < 0) + goto exit; + } + if (errno != ENOENT) + ret = -errno; +exit: + if (actx) + ntfs_attr_put_search_ctx(actx); + if (ntfs_inode_close(ni)) + set_fuse_error(&ret); + return ret; +} + +static int ntfs_fuse_getxattr_windows(const char *path, const char *name, + char *value, size_t size) +{ + ntfs_attr_search_ctx *actx = NULL; + ntfs_inode *ni; + char *to = value; + int ret = 0; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + struct SECURITY_CONTEXT security; +#endif + + if (strcmp(name, "ntfs.streams.list")) + return -EOPNOTSUPP; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* parent directory must be executable */ + if (ntfs_fuse_fill_security_context(&security) + && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, + (ntfs_inode*)NULL,S_IEXEC)) { + return (-errno); + } +#endif + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + if (!ntfs_allowed_access(&security,ni,S_IREAD)) { + ret = -errno; + goto exit; + } +#endif + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (!actx) { + ret = -errno; + goto exit; + } + while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, + 0, NULL, 0, actx)) { + char *tmp_name = NULL; + int tmp_name_len; + + if (!actx->attr->name_length) + continue; + tmp_name_len = ntfs_ucstombs((ntfschar *)((u8*)actx->attr + + le16_to_cpu(actx->attr->name_offset)), + actx->attr->name_length, &tmp_name, 0); + if (tmp_name_len < 0) { + ret = -errno; + goto exit; + } + if (ret) + ret++; /* For space delimiter. */ + ret += tmp_name_len; + if (size) { + if ((size_t)ret <= size) { + /* Don't add space to the beginning of line. */ + if (to != value) { + *to = '\0'; + to++; + } + strncpy(to, tmp_name, tmp_name_len); + to += tmp_name_len; + } else { + free(tmp_name); + ret = -ERANGE; + goto exit; + } + } + free(tmp_name); + } + if (errno != ENOENT) + ret = -errno; +exit: + if (actx) + ntfs_attr_put_search_ctx(actx); + if (ntfs_inode_close(ni)) + set_fuse_error(&ret); + return ret; +} + +static int ntfs_fuse_getxattr(const char *path, const char *name, + char *value, size_t size) +{ + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_attr *na = NULL; + ntfschar *lename = NULL; + int res, lename_len; + s64 rsize; + enum SYSTEMXATTRS attr; + int namespace; + struct SECURITY_CONTEXT security; + + attr = ntfs_xattr_system_type(name,ctx->vol); + if (attr != XATTR_UNMAPPED) { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* + * hijack internal data and ACL retrieval, whatever + * mode was selected for xattr (from the user's + * point of view, ACLs are not xattr) + */ + ni = ntfs_check_access_xattr(&security, path, attr, FALSE); + if (ni) { + if (ntfs_allowed_access(&security,ni,S_IREAD)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = get_parent_dir(path); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_getxattr(&security, + attr, ni, dir_ni, value, size); + if (dir_ni && ntfs_inode_close(dir_ni)) + set_fuse_error(&res); + } else { + res = -errno; + } + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } else + res = -errno; +#else + /* + * Only hijack NTFS ACL retrieval if POSIX ACLS + * option is not selected + * Access control is done by fuse + */ + if (ntfs_fuse_is_named_data_stream(path)) + res = -EINVAL; /* n/a for named data streams. */ + else { + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (ni) { + /* user mapping not mandatory */ + ntfs_fuse_fill_security_context(&security); + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = get_parent_dir(path); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_getxattr(&security, + attr, ni, dir_ni, value, size); + if (dir_ni && ntfs_inode_close(dir_ni)) + set_fuse_error(&res); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } else + res = -errno; + } +#endif + return (res); + } + if (ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) + return ntfs_fuse_getxattr_windows(path, name, value, size); + if (ctx->streams == NF_STREAMS_INTERFACE_NONE) + return -EOPNOTSUPP; + namespace = xattr_namespace(name); + if (namespace == XATTRNS_NONE) + return -EOPNOTSUPP; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* parent directory must be executable */ + if (ntfs_fuse_fill_security_context(&security) + && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, + (ntfs_inode*)NULL,S_IEXEC)) { + return (-errno); + } + /* trusted only readable by root */ + if ((namespace == XATTRNS_TRUSTED) + && security.uid) + return -ENODATA; +#endif + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; + /* Return with no result for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + res = -ENODATA; + goto exit; + } + /* otherwise file must be readable */ +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + if (!ntfs_allowed_access(&security, ni, S_IREAD)) { + res = -errno; + goto exit; + } +#endif + lename_len = fix_xattr_prefix(name, namespace, &lename); + if (lename_len == -1) { + res = -errno; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, lename, lename_len); + if (!na) { + res = -ENODATA; + goto exit; + } + rsize = na->data_size; + if (ctx->efs_raw + && rsize + && (na->data_flags & ATTR_IS_ENCRYPTED) + && NAttrNonResident(na)) + rsize = ((na->data_size + 511) & ~511) + 2; + if (size) { + if (size >= (size_t)rsize) { + res = ntfs_attr_pread(na, 0, rsize, value); + if (res != rsize) + res = -errno; + } else + res = -ERANGE; + } else + res = rsize; +exit: + if (na) + ntfs_attr_close(na); + free(lename); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +static int ntfs_fuse_setxattr(const char *path, const char *name, + const char *value, size_t size, int flags) +{ + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_attr *na = NULL; + ntfschar *lename = NULL; + int res, lename_len; + size_t total; + s64 part; + enum SYSTEMXATTRS attr; + int namespace; + struct SECURITY_CONTEXT security; + + attr = ntfs_xattr_system_type(name,ctx->vol); + if (attr != XATTR_UNMAPPED) { +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* + * hijack internal data and ACL setting, whatever + * mode was selected for xattr (from the user's + * point of view, ACLs are not xattr) + * Note : ctime updated on successful settings + */ + ni = ntfs_check_access_xattr(&security,path,attr,TRUE); + if (ni) { + if (ntfs_allowed_as_owner(&security,ni)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = get_parent_dir(path); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_setxattr(&security, + attr, ni, dir_ni, value, size, flags); + /* never have to close dir_ni */ + if (res) + res = -errno; + } else + res = -errno; + if (attr != XATTR_NTFS_DOS_NAME) { + if (!res) + ntfs_fuse_update_times(ni, + NTFS_UPDATE_CTIME); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } else + res = -errno; +#else + /* + * Only hijack NTFS ACL setting if POSIX ACLS + * option is not selected + * Access control is partially done by fuse + */ + if (ntfs_fuse_is_named_data_stream(path)) + res = -EINVAL; /* n/a for named data streams. */ + else { + /* creation of a new name is not controlled by fuse */ + if (attr == XATTR_NTFS_DOS_NAME) + ni = ntfs_check_access_xattr(&security,path,attr,TRUE); + else + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (ni) { + /* + * user mapping is not mandatory + * if defined, only owner is allowed + */ + if (!ntfs_fuse_fill_security_context(&security) + || ntfs_allowed_as_owner(&security,ni)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = get_parent_dir(path); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_setxattr(&security, + attr, ni, dir_ni, value, + size, flags); + /* never have to close dir_ni */ + if (res) + res = -errno; + } else + res = -errno; + if (attr != XATTR_NTFS_DOS_NAME) { + if (!res) + ntfs_fuse_update_times(ni, + NTFS_UPDATE_CTIME); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } else + res = -errno; + } +#endif + return (res); + } + if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR) + && (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR)) + return -EOPNOTSUPP; + namespace = xattr_namespace(name); + if (namespace == XATTRNS_NONE) + return -EOPNOTSUPP; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* parent directory must be executable */ + if (ntfs_fuse_fill_security_context(&security) + && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, + (ntfs_inode*)NULL,S_IEXEC)) { + return (-errno); + } + /* security and trusted only settable by root */ + if (((namespace == XATTRNS_SECURITY) + || (namespace == XATTRNS_TRUSTED)) + && security.uid) + return -EPERM; +#endif + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + switch (namespace) { + case XATTRNS_SECURITY : + case XATTRNS_TRUSTED : + if (security.uid) { + res = -EPERM; + goto exit; + } + break; + case XATTRNS_SYSTEM : + if (!ntfs_allowed_as_owner(&security,ni)) { + res = -EACCES; + goto exit; + } + break; + default : + /* User xattr not allowed for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + res = -EPERM; + goto exit; + } + if (!ntfs_allowed_access(&security,ni,S_IWRITE)) { + res = -EACCES; + goto exit; + } + break; + } +#else + /* User xattr not allowed for symlinks, fifo, etc. */ + if ((namespace == XATTRNS_USER) + && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { + res = -EPERM; + goto exit; + } +#endif + lename_len = fix_xattr_prefix(name, namespace, &lename); + if ((lename_len == -1) + || (ctx->windows_names + && ntfs_forbidden_chars(lename,lename_len))) { + res = -errno; + goto exit; + } + na = ntfs_attr_open(ni, AT_DATA, lename, lename_len); + if (na && flags == XATTR_CREATE) { + res = -EEXIST; + goto exit; + } + if (!na) { + if (flags == XATTR_REPLACE) { + res = -ENODATA; + goto exit; + } + if (ntfs_attr_add(ni, AT_DATA, lename, lename_len, NULL, 0)) { + res = -errno; + goto exit; + } + if (!(ni->flags & FILE_ATTR_ARCHIVE)) { + set_archive(ni); + NInoFileNameSetDirty(ni); + } + na = ntfs_attr_open(ni, AT_DATA, lename, lename_len); + if (!na) { + res = -errno; + goto exit; + } + } else { + /* currently compressed streams can only be wiped out */ + if (ntfs_attr_truncate(na, (s64)0 /* size */)) { + res = -errno; + goto exit; + } + } + total = 0; + res = 0; + if (size) { + do { + part = ntfs_attr_pwrite(na, total, size - total, + &value[total]); + if (part > 0) + total += part; + } while ((part > 0) && (total < size)); + } + if ((total != size) || ntfs_attr_pclose(na)) + res = -errno; + else { + if (ctx->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED)) { + if (ntfs_efs_fixup_attribute(NULL,na)) + res = -errno; + } + } + if (!res) { + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + if (!(ni->flags & FILE_ATTR_ARCHIVE)) { + set_archive(ni); + NInoFileNameSetDirty(ni); + } + } +exit: + if (na) + ntfs_attr_close(na); + free(lename); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +static int ntfs_fuse_removexattr(const char *path, const char *name) +{ + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfschar *lename = NULL; + int res = 0, lename_len; + enum SYSTEMXATTRS attr; + int namespace; + struct SECURITY_CONTEXT security; + + attr = ntfs_xattr_system_type(name,ctx->vol); + if (attr != XATTR_UNMAPPED) { + switch (attr) { + /* + * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES + * is never allowed + */ + case XATTR_NTFS_ACL : + case XATTR_NTFS_ATTRIB : + case XATTR_NTFS_ATTRIB_BE : + case XATTR_NTFS_EFSINFO : + case XATTR_NTFS_TIMES : + case XATTR_NTFS_TIMES_BE : + case XATTR_NTFS_CRTIME : + case XATTR_NTFS_CRTIME_BE : + res = -EPERM; + break; + default : +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* + * hijack internal data and ACL removal, whatever + * mode was selected for xattr (from the user's + * point of view, ACLs are not xattr) + * Note : ctime updated on successful settings + */ + ni = ntfs_check_access_xattr(&security,path,attr,TRUE); + if (ni) { + if (ntfs_allowed_as_owner(&security,ni)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = get_parent_dir(path); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_removexattr(&security, + attr, ni, dir_ni); + /* never have to close dir_ni */ + if (res) + res = -errno; + } else + res = -errno; + if (attr != XATTR_NTFS_DOS_NAME) { + if (!res) + ntfs_fuse_update_times(ni, + NTFS_UPDATE_CTIME); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } else + res = -errno; +#else + /* + * Only hijack NTFS ACL setting if POSIX ACLS + * option is not selected + * Access control is partially done by fuse + */ + /* creation of a new name is not controlled by fuse */ + if (attr == XATTR_NTFS_DOS_NAME) + ni = ntfs_check_access_xattr(&security, + path, attr, TRUE); + else { + if (ntfs_fuse_is_named_data_stream(path)) { + ni = (ntfs_inode*)NULL; + errno = EINVAL; /* n/a for named data streams. */ + } else + ni = ntfs_pathname_to_inode(ctx->vol, + NULL, path); + } + if (ni) { + /* + * user mapping is not mandatory + * if defined, only owner is allowed + */ + if (!ntfs_fuse_fill_security_context(&security) + || ntfs_allowed_as_owner(&security,ni)) { + if (attr == XATTR_NTFS_DOS_NAME) + dir_ni = get_parent_dir(path); + else + dir_ni = (ntfs_inode*)NULL; + res = ntfs_xattr_system_removexattr(&security, + attr, ni, dir_ni); + /* never have to close dir_ni */ + if (res) + res = -errno; + } else + res = -errno; + if (attr != XATTR_NTFS_DOS_NAME) { + if (!res) + ntfs_fuse_update_times(ni, + NTFS_UPDATE_CTIME); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + } + } else + res = -errno; +#endif + break; + } + return (res); + } + if ((ctx->streams != NF_STREAMS_INTERFACE_XATTR) + && (ctx->streams != NF_STREAMS_INTERFACE_OPENXATTR)) + return -EOPNOTSUPP; + namespace = xattr_namespace(name); + if (namespace == XATTRNS_NONE) + return -EOPNOTSUPP; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + /* parent directory must be executable */ + if (ntfs_fuse_fill_security_context(&security) + && !ntfs_allowed_dir_access(&security,path,(ntfs_inode*)NULL, + (ntfs_inode*)NULL,S_IEXEC)) { + return (-errno); + } + /* security and trusted only settable by root */ + if (((namespace == XATTRNS_SECURITY) + || (namespace == XATTRNS_TRUSTED)) + && security.uid) + return -EACCES; +#endif + ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!ni) + return -errno; +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + switch (namespace) { + case XATTRNS_SECURITY : + case XATTRNS_TRUSTED : + if (security.uid) { + res = -EPERM; + goto exit; + } + break; + case XATTRNS_SYSTEM : + if (!ntfs_allowed_as_owner(&security,ni)) { + res = -EACCES; + goto exit; + } + break; + default : + /* User xattr not allowed for symlinks, fifo, etc. */ + if (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT)) { + res = -EPERM; + goto exit; + } + if (!ntfs_allowed_access(&security,ni,S_IWRITE)) { + res = -EACCES; + goto exit; + } + break; + } +#else + /* User xattr not allowed for symlinks, fifo, etc. */ + if ((namespace == XATTRNS_USER) + && (ni->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_REPARSE_POINT))) { + res = -EPERM; + goto exit; + } +#endif + lename_len = fix_xattr_prefix(name, namespace, &lename); + if (lename_len == -1) { + res = -errno; + goto exit; + } + if (ntfs_attr_remove(ni, AT_DATA, lename, lename_len)) { + if (errno == ENOENT) + errno = ENODATA; + res = -errno; + } + if (!res) { + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + if (!(ni->flags & FILE_ATTR_ARCHIVE)) { + set_archive(ni); + NInoFileNameSetDirty(ni); + } + } +exit: + free(lename); + if (ntfs_inode_close(ni)) + set_fuse_error(&res); + return res; +} + +#else +#if POSIXACLS +#error "Option inconsistency : POSIXACLS requires SETXATTR" +#endif +#endif /* HAVE_SETXATTR */ + +static void ntfs_close(void) +{ + struct SECURITY_CONTEXT security; + + if (!ctx) + return; + + if (!ctx->vol) + return; + + if (ctx->mounted) { + ntfs_log_info("Unmounting %s (%s)\n", opts.device, + ctx->vol->vol_name); + if (ntfs_fuse_fill_security_context(&security)) { + if (ctx->seccache && ctx->seccache->head.p_reads) { + ntfs_log_info("Permissions cache : %lu writes, " + "%lu reads, %lu.%1lu%% hits\n", + ctx->seccache->head.p_writes, + ctx->seccache->head.p_reads, + 100 * ctx->seccache->head.p_hits + / ctx->seccache->head.p_reads, + 1000 * ctx->seccache->head.p_hits + / ctx->seccache->head.p_reads % 10); + } + } + ntfs_close_secure(&security); + } + + if (ntfs_umount(ctx->vol, FALSE)) + ntfs_log_perror("Failed to close volume %s", opts.device); + + ctx->vol = NULL; +} + +static void ntfs_fuse_destroy2(void *unused __attribute__((unused))) +{ + ntfs_close(); +} + +static struct fuse_operations ntfs_3g_ops = { + .getattr = ntfs_fuse_getattr, + .readlink = ntfs_fuse_readlink, + .readdir = ntfs_fuse_readdir, + .open = ntfs_fuse_open, + .release = ntfs_fuse_release, + .read = ntfs_fuse_read, + .write = ntfs_fuse_write, + .truncate = ntfs_fuse_truncate, + .ftruncate = ntfs_fuse_ftruncate, + .statfs = ntfs_fuse_statfs, + .chmod = ntfs_fuse_chmod, + .chown = ntfs_fuse_chown, + .create = ntfs_fuse_create_file, + .mknod = ntfs_fuse_mknod, + .symlink = ntfs_fuse_symlink, + .link = ntfs_fuse_link, + .unlink = ntfs_fuse_unlink, + .rename = ntfs_fuse_rename, + .mkdir = ntfs_fuse_mkdir, + .rmdir = ntfs_fuse_rmdir, +#ifdef HAVE_UTIMENSAT + .utimens = ntfs_fuse_utimens, +#else + .utime = ntfs_fuse_utime, +#endif + .fsync = ntfs_fuse_fsync, + .fsyncdir = ntfs_fuse_fsync, + .bmap = ntfs_fuse_bmap, + .destroy = ntfs_fuse_destroy2, +#if defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28) + .ioctl = ntfs_fuse_ioctl, +#endif /* defined(FUSE_INTERNAL) || (FUSE_VERSION >= 28) */ +#if !KERNELPERMS | (POSIXACLS & !KERNELACLS) + .access = ntfs_fuse_access, + .opendir = ntfs_fuse_opendir, +#endif +#ifdef HAVE_SETXATTR + .getxattr = ntfs_fuse_getxattr, + .setxattr = ntfs_fuse_setxattr, + .removexattr = ntfs_fuse_removexattr, + .listxattr = ntfs_fuse_listxattr, +#endif /* HAVE_SETXATTR */ +#if defined(__APPLE__) || defined(__DARWIN__) + /* MacFUSE extensions. */ + .getxtimes = ntfs_macfuse_getxtimes, + .setcrtime = ntfs_macfuse_setcrtime, + .setbkuptime = ntfs_macfuse_setbkuptime, + .setchgtime = ntfs_macfuse_setchgtime, +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + .init = ntfs_init +}; + +static int ntfs_fuse_init(void) +{ + ctx = ntfs_calloc(sizeof(ntfs_fuse_context_t)); + if (!ctx) + return -1; + + *ctx = (ntfs_fuse_context_t) { + .uid = getuid(), + .gid = getgid(), +#if defined(linux) + .streams = NF_STREAMS_INTERFACE_XATTR, +#else + .streams = NF_STREAMS_INTERFACE_NONE, +#endif + .atime = ATIME_RELATIVE, + .silent = TRUE, + .recover = TRUE + }; + return 0; +} + +static int ntfs_open(const char *device) +{ + unsigned long flags = 0; + + if (!ctx->blkdev) + flags |= NTFS_MNT_EXCLUSIVE; + if (ctx->ro) + flags |= NTFS_MNT_RDONLY; + if (ctx->recover) + flags |= NTFS_MNT_RECOVER; + if (ctx->hiberfile) + flags |= NTFS_MNT_IGNORE_HIBERFILE; + + ctx->vol = ntfs_mount(device, flags); + if (!ctx->vol) { + ntfs_log_perror("Failed to mount '%s'", device); + goto err_out; + } + if (ctx->sync && ctx->vol->dev) + NDevSetSync(ctx->vol->dev); + if (ctx->compression) + NVolSetCompression(ctx->vol); + else + NVolClearCompression(ctx->vol); +#ifdef HAVE_SETXATTR + /* archivers must see hidden files */ + if (ctx->efs_raw) + ctx->hide_hid_files = FALSE; +#endif + if (ntfs_set_shown_files(ctx->vol, ctx->show_sys_files, + !ctx->hide_hid_files, ctx->hide_dot_files)) + goto err_out; + + ctx->vol->free_clusters = ntfs_attr_get_free_bits(ctx->vol->lcnbmp_na); + if (ctx->vol->free_clusters < 0) { + ntfs_log_perror("Failed to read NTFS $Bitmap"); + goto err_out; + } + + ctx->vol->free_mft_records = ntfs_get_nr_free_mft_records(ctx->vol); + if (ctx->vol->free_mft_records < 0) { + ntfs_log_perror("Failed to calculate free MFT records"); + goto err_out; + } + + if (ctx->hiberfile && ntfs_volume_check_hiberfile(ctx->vol, 0)) { + if (errno != EPERM) + goto err_out; + if (ntfs_fuse_rm("/hiberfil.sys")) + goto err_out; + } + + errno = 0; +err_out: + return ntfs_volume_error(errno); + +} + +static void usage(void) +{ + ntfs_log_info(usage_msg, EXEC_NAME, VERSION, FUSE_TYPE, fuse_version(), + 4 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING, + EXEC_NAME, ntfs_home); +} + +#if defined(linux) || defined(__uClinux__) + +static const char *dev_fuse_msg = +"HINT: You should be root, or make ntfs-3g setuid root, or load the FUSE\n" +" kernel module as root ('modprobe fuse' or 'insmod <path_to>/fuse.ko'" +" or insmod <path_to>/fuse.o'). Make also sure that the fuse device" +" exists. It's usually either /dev/fuse or /dev/misc/fuse."; + +static const char *fuse26_kmod_msg = +"WARNING: Deficient Linux kernel detected. Some driver features are\n" +" not available (swap file on NTFS, boot from NTFS by LILO), and\n" +" unmount is not safe unless it's made sure the ntfs-3g process\n" +" naturally terminates after calling 'umount'. If you wish this\n" +" message to disappear then you should upgrade to at least kernel\n" +" version 2.6.20, or request help from your distribution to fix\n" +" the kernel problem. The below web page has more information:\n" +" http://tuxera.com/community/ntfs-3g-faq/#fuse26\n" +"\n"; + +static void mknod_dev_fuse(const char *dev) +{ + struct stat st; + + if (stat(dev, &st) && (errno == ENOENT)) { + mode_t mask = umask(0); + if (mknod(dev, S_IFCHR | 0666, makedev(10, 229))) { + ntfs_log_perror("Failed to create '%s'", dev); + if (errno == EPERM) + ntfs_log_error("%s", dev_fuse_msg); + } + umask(mask); + } +} + +static void create_dev_fuse(void) +{ + mknod_dev_fuse("/dev/fuse"); + +#ifdef __UCLIBC__ + { + struct stat st; + /* The fuse device is under /dev/misc using devfs. */ + if (stat("/dev/misc", &st) && (errno == ENOENT)) { + mode_t mask = umask(0); + mkdir("/dev/misc", 0775); + umask(mask); + } + mknod_dev_fuse("/dev/misc/fuse"); + } +#endif +} + +static fuse_fstype get_fuse_fstype(void) +{ + char buf[256]; + fuse_fstype fstype = FSTYPE_NONE; + + FILE *f = fopen("/proc/filesystems", "r"); + if (!f) { + ntfs_log_perror("Failed to open /proc/filesystems"); + return FSTYPE_UNKNOWN; + } + + while (fgets(buf, sizeof(buf), f)) { + if (strstr(buf, "fuseblk\n")) { + fstype = FSTYPE_FUSEBLK; + break; + } + if (strstr(buf, "fuse\n")) + fstype = FSTYPE_FUSE; + } + + fclose(f); + return fstype; +} + +static fuse_fstype load_fuse_module(void) +{ + int i; + struct stat st; + pid_t pid; + const char *cmd = "/sbin/modprobe"; + struct timespec req = { 0, 100000000 }; /* 100 msec */ + fuse_fstype fstype; + + if (!stat(cmd, &st) && !geteuid()) { + pid = fork(); + if (!pid) { + execl(cmd, cmd, "fuse", NULL); + _exit(1); + } else if (pid != -1) + waitpid(pid, NULL, 0); + } + + for (i = 0; i < 10; i++) { + /* + * We sleep first because despite the detection of the loaded + * FUSE kernel module, fuse_mount() can still fail if it's not + * fully functional/initialized. Note, of course this is still + * unreliable but usually helps. + */ + nanosleep(&req, NULL); + fstype = get_fuse_fstype(); + if (fstype != FSTYPE_NONE) + break; + } + return fstype; +} + +#endif + +static struct fuse_chan *try_fuse_mount(char *parsed_options) +{ + struct fuse_chan *fc = NULL; + struct fuse_args margs = FUSE_ARGS_INIT(0, NULL); + + /* The fuse_mount() options get modified, so we always rebuild it */ + if ((fuse_opt_add_arg(&margs, EXEC_NAME) == -1 || + fuse_opt_add_arg(&margs, "-o") == -1 || + fuse_opt_add_arg(&margs, parsed_options) == -1)) { + ntfs_log_error("Failed to set FUSE options.\n"); + goto free_args; + } + + fc = fuse_mount(opts.mnt_point, &margs); +free_args: + fuse_opt_free_args(&margs); + return fc; + +} + +static int set_fuseblk_options(char **parsed_options) +{ + char options[64]; + long pagesize; + u32 blksize = ctx->vol->cluster_size; + + pagesize = sysconf(_SC_PAGESIZE); + if (pagesize < 1) + pagesize = 4096; + + if (blksize > (u32)pagesize) + blksize = pagesize; + + snprintf(options, sizeof(options), ",blkdev,blksize=%u", blksize); + if (ntfs_strappend(parsed_options, options)) + return -1; + return 0; +} + +static struct fuse *mount_fuse(char *parsed_options) +{ + struct fuse *fh = NULL; + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + + ctx->fc = try_fuse_mount(parsed_options); + if (!ctx->fc) + return NULL; + + if (fuse_opt_add_arg(&args, "") == -1) + goto err; +#if !CACHEING + if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=0") == -1) + goto err; +#else + if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=1") == -1) + goto err; +#endif + if (ctx->debug) + if (fuse_opt_add_arg(&args, "-odebug") == -1) + goto err; + + fh = fuse_new(ctx->fc, &args , &ntfs_3g_ops, sizeof(ntfs_3g_ops), NULL); + if (!fh) + goto err; + + if (fuse_set_signal_handlers(fuse_get_session(fh))) + goto err_destory; +out: + fuse_opt_free_args(&args); + return fh; +err_destory: + fuse_destroy(fh); + fh = NULL; +err: + fuse_unmount(opts.mnt_point, ctx->fc); + goto out; +} + +static void setup_logging(char *parsed_options) +{ + if (!ctx->no_detach) { + if (daemon(0, ctx->debug)) + ntfs_log_error("Failed to daemonize.\n"); + else if (!ctx->debug) { +#ifndef DEBUG + ntfs_log_set_handler(ntfs_log_handler_syslog); + /* Override default libntfs identify. */ + openlog(EXEC_NAME, LOG_PID, LOG_DAEMON); +#endif + } + } + + ctx->seccache = (struct PERMISSIONS_CACHE*)NULL; + + ntfs_log_info("Version %s %s %d\n", VERSION, FUSE_TYPE, fuse_version()); + if (strcmp(opts.arg_device,opts.device)) + ntfs_log_info("Requested device %s canonicalized as %s\n", + opts.arg_device,opts.device); + ntfs_log_info("Mounted %s (%s, label \"%s\", NTFS %d.%d)\n", + opts.device, (ctx->ro) ? "Read-Only" : "Read-Write", + ctx->vol->vol_name, ctx->vol->major_ver, + ctx->vol->minor_ver); + ntfs_log_info("Cmdline options: %s\n", opts.options ? opts.options : ""); + ntfs_log_info("Mount options: %s\n", parsed_options); +} + +int main(int argc, char *argv[]) +{ + char *parsed_options = NULL; + struct fuse *fh; +#if !(defined(__sun) && defined (__SVR4)) + fuse_fstype fstype = FSTYPE_UNKNOWN; +#endif + const char *permissions_mode = (const char*)NULL; + const char *failed_secure = (const char*)NULL; +#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) + struct XATTRMAPPING *xattr_mapping = (struct XATTRMAPPING*)NULL; +#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ + struct stat sbuf; + unsigned long existing_mount; + int err, fd; + + /* + * Make sure file descriptors 0, 1 and 2 are open, + * otherwise chaos would ensue. + */ + do { + fd = open("/dev/null", O_RDWR); + if (fd > 2) + close(fd); + } while (fd >= 0 && fd <= 2); + +#ifndef FUSE_INTERNAL + if ((getuid() != geteuid()) || (getgid() != getegid())) { + fprintf(stderr, "%s", setuid_msg); + return NTFS_VOLUME_INSECURE; + } +#endif + if (drop_privs()) + return NTFS_VOLUME_NO_PRIVILEGE; + + ntfs_set_locale(); + ntfs_log_set_handler(ntfs_log_handler_stderr); + + if (ntfs_parse_options(&opts, usage, argc, argv)) { + usage(); + return NTFS_VOLUME_SYNTAX_ERROR; + } + + if (ntfs_fuse_init()) { + err = NTFS_VOLUME_OUT_OF_MEMORY; + goto err2; + } + + parsed_options = parse_mount_options(ctx, &opts, FALSE); + if (!parsed_options) { + err = NTFS_VOLUME_SYNTAX_ERROR; + goto err_out; + } + if (!ntfs_check_if_mounted(opts.device,&existing_mount) + && (existing_mount & NTFS_MF_MOUNTED) + /* accept multiple read-only mounts */ + && (!(existing_mount & NTFS_MF_READONLY) || !ctx->ro)) { + err = NTFS_VOLUME_LOCKED; + goto err_out; + } + + /* need absolute mount point for junctions */ + if (opts.mnt_point[0] == '/') + ctx->abs_mnt_point = strdup(opts.mnt_point); + else { + ctx->abs_mnt_point = (char*)ntfs_malloc(PATH_MAX); + if (ctx->abs_mnt_point) { + if (getcwd(ctx->abs_mnt_point, + PATH_MAX - strlen(opts.mnt_point) - 1)) { + strcat(ctx->abs_mnt_point, "/"); + strcat(ctx->abs_mnt_point, opts.mnt_point); +#if defined(__sun) && defined (__SVR4) + /* Solaris also wants the absolute mount point */ + opts.mnt_point = ctx->abs_mnt_point; +#endif /* defined(__sun) && defined (__SVR4) */ + } + } + } + if (!ctx->abs_mnt_point) { + err = NTFS_VOLUME_OUT_OF_MEMORY; + goto err_out; + } + + ctx->security.uid = 0; + ctx->security.gid = 0; + if ((opts.mnt_point[0] == '/') + && !stat(opts.mnt_point,&sbuf)) { + /* collect owner of mount point, useful for default mapping */ + ctx->security.uid = sbuf.st_uid; + ctx->security.gid = sbuf.st_gid; + } + +#if defined(linux) || defined(__uClinux__) + fstype = get_fuse_fstype(); + + err = NTFS_VOLUME_NO_PRIVILEGE; + if (restore_privs()) + goto err_out; + + if (fstype == FSTYPE_NONE || fstype == FSTYPE_UNKNOWN) + fstype = load_fuse_module(); + create_dev_fuse(); + + if (drop_privs()) + goto err_out; +#endif + if (stat(opts.device, &sbuf)) { + ntfs_log_perror("Failed to access '%s'", opts.device); + err = NTFS_VOLUME_NO_PRIVILEGE; + goto err_out; + } + +#if !(defined(__sun) && defined (__SVR4)) + /* Always use fuseblk for block devices unless it's surely missing. */ + if (S_ISBLK(sbuf.st_mode) && (fstype != FSTYPE_FUSE)) + ctx->blkdev = TRUE; +#endif + +#ifndef FUSE_INTERNAL + if (getuid() && ctx->blkdev) { + ntfs_log_error("%s", unpriv_fuseblk_msg); + err = NTFS_VOLUME_NO_PRIVILEGE; + goto err2; + } +#endif + err = ntfs_open(opts.device); + if (err) + goto err_out; + + /* Force read-only mount if the device was found read-only */ + if (!ctx->ro && NVolReadOnly(ctx->vol)) { + ctx->ro = TRUE; + if (ntfs_strinsert(&parsed_options, ",ro")) + goto err_out; + } + /* We must do this after ntfs_open() to be able to set the blksize */ + if (ctx->blkdev && set_fuseblk_options(&parsed_options)) + goto err_out; + + ctx->security.vol = ctx->vol; + ctx->vol->secure_flags = ctx->secure_flags; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + ctx->vol->efs_raw = ctx->efs_raw; +#endif /* HAVE_SETXATTR */ + /* JPA open $Secure, (whatever NTFS version !) */ + /* to initialize security data */ + if (ntfs_open_secure(ctx->vol) && (ctx->vol->major_ver >= 3)) + failed_secure = "Could not open file $Secure"; + if (!ntfs_build_mapping(&ctx->security,ctx->usermap_path, + (ctx->vol->secure_flags + & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL))) + && !ctx->inherit + && !(ctx->vol->secure_flags & (1 << SECURITY_WANTED)))) { +#if POSIXACLS + /* use basic permissions if requested */ + if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT)) + permissions_mode = "User mapping built, Posix ACLs not used"; + else { + permissions_mode = "User mapping built, Posix ACLs in use"; +#if KERNELACLS + if (ntfs_strinsert(&parsed_options, ",default_permissions,acl")) { + err = NTFS_VOLUME_SYNTAX_ERROR; + goto err_out; + } +#endif /* KERNELACLS */ + } +#else /* POSIXACLS */ +#if KERNELPERMS + if (!(ctx->vol->secure_flags + & ((1 << SECURITY_DEFAULT) | (1 << SECURITY_ACL)))) { + /* + * No explicit option but user mapping found + * force default security + */ + ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT); + if (ntfs_strinsert(&parsed_options, ",default_permissions")) { + err = NTFS_VOLUME_SYNTAX_ERROR; + goto err_out; + } + } +#endif /* KERNELPERMS */ + permissions_mode = "User mapping built"; +#endif /* POSIXACLS */ + ctx->dmask = ctx->fmask = 0; + } else { + ctx->security.uid = ctx->uid; + ctx->security.gid = ctx->gid; + /* same ownership/permissions for all files */ + ctx->security.mapping[MAPUSERS] = (struct MAPPING*)NULL; + ctx->security.mapping[MAPGROUPS] = (struct MAPPING*)NULL; + if ((ctx->vol->secure_flags & (1 << SECURITY_WANTED)) + && !(ctx->vol->secure_flags & (1 << SECURITY_DEFAULT))) { + ctx->vol->secure_flags |= (1 << SECURITY_DEFAULT); + if (ntfs_strinsert(&parsed_options, ",default_permissions")) { + err = NTFS_VOLUME_SYNTAX_ERROR; + goto err_out; + } + } + if (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT)) { + ctx->vol->secure_flags |= (1 << SECURITY_RAW); + permissions_mode = "Global ownership and permissions enforced"; + } else { + ctx->vol->secure_flags &= ~(1 << SECURITY_RAW); + permissions_mode = "Ownership and permissions disabled"; + } + } + if (ctx->usermap_path) + free (ctx->usermap_path); + +#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) + xattr_mapping = ntfs_xattr_build_mapping(ctx->vol, + ctx->xattrmap_path); + ctx->vol->xattr_mapping = xattr_mapping; + /* + * Errors are logged, do not refuse mounting, it would be + * too difficult to fix the unmountable mapping file. + */ + if (ctx->xattrmap_path) + free(ctx->xattrmap_path); +#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ + + fh = mount_fuse(parsed_options); + if (!fh) { + err = NTFS_VOLUME_FUSE_ERROR; + goto err_out; + } + + ctx->mounted = TRUE; + +#if defined(linux) || defined(__uClinux__) + if (S_ISBLK(sbuf.st_mode) && (fstype == FSTYPE_FUSE)) + ntfs_log_info("%s", fuse26_kmod_msg); +#endif + setup_logging(parsed_options); + if (failed_secure) + ntfs_log_info("%s\n",failed_secure); + if (permissions_mode) + ntfs_log_info("%s, configuration type %d\n",permissions_mode, + 4 + POSIXACLS*6 - KERNELPERMS*3 + CACHEING); + if ((ctx->vol->secure_flags & (1 << SECURITY_RAW)) + && !ctx->uid && ctx->gid) + ntfs_log_error("Warning : using problematic uid==0 and gid!=0\n"); + + fuse_loop(fh); + + err = 0; + + fuse_unmount(opts.mnt_point, ctx->fc); + fuse_destroy(fh); +err_out: + ntfs_mount_error(opts.device, opts.mnt_point, err); + if (ctx->abs_mnt_point) + free(ctx->abs_mnt_point); +#if defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) + ntfs_xattr_free_mapping(xattr_mapping); +#endif /* defined(HAVE_SETXATTR) && defined(XATTR_MAPPINGS) */ +err2: + ntfs_close(); + free(ctx); + free(parsed_options); + free(opts.options); + free(opts.device); + return err; +} diff --git a/src/ntfs-3g.probe.8 b/src/ntfs-3g.probe.8 new file mode 100755 index 0000000000000000000000000000000000000000..5215f8a0a507a68bf3c883e078e8a45526876e74 --- /dev/null +++ b/src/ntfs-3g.probe.8 @@ -0,0 +1,81 @@ +.\" Copyright (c) 2008 Szabolcs Szakacsits. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFS-3G.PROBE 8 "January 2008" "ntfs-3g.probe 2015.3.14" +.SH NAME +ntfs-3g.probe \- Probe an NTFS volume mountability +.SH SYNOPSIS +.B ntfs-3g.probe +.I <\-\-readonly|\-\-readwrite> +.I volume +.br +.SH DESCRIPTION +The \fBntfs-3g.probe\fR utility tests a volume if it's NTFS mountable +read-only or read-write, and exits with a status value accordingly. +The \fIvolume\fR can be a block device or image file. +.SH OPTIONS +Below is a summary of the options that \fBntfs-3g.probe\fR accepts. +.TP +.B \-r, \-\-readonly +Test if the volume can be mounted read-only. +.TP +.B \-w, \-\-readwrite +Test if the volume can be mounted read-write. +.TP +.B \-h, \-\-help +Display help and exit. +.SH EXAMPLE +Test if /dev/sda1 can be mounted read-write: +.RS +.sp +.B ntfs-3g.probe --readwrite /dev/sda1 +.sp +.RE +.SH EXIT CODES +The exit codes are as follows: +.IP 0 +Volume is mountable. +.IP 11 +Syntax error, command line parsing failed. +.IP 12 +The volume doesn't have a valid NTFS. +.IP 13 +Inconsistent NTFS, hardware or device driver fault, or unsetup +SoftRAID/FakeRAID hardware. +.IP 14 +The NTFS partition is hibernated. +.IP 15 +The volume was not cleanly unmounted. +.IP 16 +The volume is already exclusively opened and in use by a kernel +driver or software. +.IP 17 +Unsetup SoftRAID/FakeRAID hardware. +.IP 18 +Unknown reason. +.IP 19 +Not enough privilege to mount. +.IP 20 +Out of memory. +.IP 21 +Unclassified FUSE error. +.SH KNOWN ISSUES +Please see +.RS +.sp +http://tuxera.com/community/ntfs-3g-faq/ +.sp +.RE +for common questions and known issues. +If you think you have found an undocumented problem in the latest release of +the software then please send an email describing it in detail. +You can contact the development team on the ntfs\-3g\-devel@lists.sf.net +address. +.SH AUTHORS +.B ntfs-3g.probe +was written by Szabolcs Szakacsits. +.SH THANKS +Alon Bar-Lev has integrated the utility into the NTFS-3G build process and +tested it with Erik Larsson before the public release. +.SH SEE ALSO +.BR ntfs-3g (8) diff --git a/src/ntfs-3g.probe.8.in b/src/ntfs-3g.probe.8.in new file mode 100755 index 0000000000000000000000000000000000000000..62ce57e5e50d54e41f844db8f979912ca499f60d --- /dev/null +++ b/src/ntfs-3g.probe.8.in @@ -0,0 +1,81 @@ +.\" Copyright (c) 2008 Szabolcs Szakacsits. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFS-3G.PROBE 8 "January 2008" "ntfs-3g.probe @VERSION@" +.SH NAME +ntfs-3g.probe \- Probe an NTFS volume mountability +.SH SYNOPSIS +.B ntfs-3g.probe +.I <\-\-readonly|\-\-readwrite> +.I volume +.br +.SH DESCRIPTION +The \fBntfs-3g.probe\fR utility tests a volume if it's NTFS mountable +read-only or read-write, and exits with a status value accordingly. +The \fIvolume\fR can be a block device or image file. +.SH OPTIONS +Below is a summary of the options that \fBntfs-3g.probe\fR accepts. +.TP +.B \-r, \-\-readonly +Test if the volume can be mounted read-only. +.TP +.B \-w, \-\-readwrite +Test if the volume can be mounted read-write. +.TP +.B \-h, \-\-help +Display help and exit. +.SH EXAMPLE +Test if /dev/sda1 can be mounted read-write: +.RS +.sp +.B ntfs-3g.probe --readwrite /dev/sda1 +.sp +.RE +.SH EXIT CODES +The exit codes are as follows: +.IP 0 +Volume is mountable. +.IP 11 +Syntax error, command line parsing failed. +.IP 12 +The volume doesn't have a valid NTFS. +.IP 13 +Inconsistent NTFS, hardware or device driver fault, or unsetup +SoftRAID/FakeRAID hardware. +.IP 14 +The NTFS partition is hibernated. +.IP 15 +The volume was not cleanly unmounted. +.IP 16 +The volume is already exclusively opened and in use by a kernel +driver or software. +.IP 17 +Unsetup SoftRAID/FakeRAID hardware. +.IP 18 +Unknown reason. +.IP 19 +Not enough privilege to mount. +.IP 20 +Out of memory. +.IP 21 +Unclassified FUSE error. +.SH KNOWN ISSUES +Please see +.RS +.sp +http://tuxera.com/community/ntfs-3g-faq/ +.sp +.RE +for common questions and known issues. +If you think you have found an undocumented problem in the latest release of +the software then please send an email describing it in detail. +You can contact the development team on the ntfs\-3g\-devel@lists.sf.net +address. +.SH AUTHORS +.B ntfs-3g.probe +was written by Szabolcs Szakacsits. +.SH THANKS +Alon Bar-Lev has integrated the utility into the NTFS-3G build process and +tested it with Erik Larsson before the public release. +.SH SEE ALSO +.BR ntfs-3g (8) diff --git a/src/ntfs-3g.probe.c b/src/ntfs-3g.probe.c new file mode 100755 index 0000000000000000000000000000000000000000..cb73aee416b70a47ee65a42f2c0ac857c8c4fe87 --- /dev/null +++ b/src/ntfs-3g.probe.c @@ -0,0 +1,166 @@ +/** + * ntfs-3g.probe - Probe NTFS volume mountability + * + * Copyright (c) 2007-2009 Szabolcs Szakacsits + * + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <getopt.h> + +#include "compat.h" +#include "volume.h" +#include "misc.h" + +typedef enum { + PROBE_UNSET, + PROBE_READONLY, + PROBE_READWRITE +} probe_t; + +static struct options { + probe_t probetype; + char *device; +} opts; + +static const char *EXEC_NAME = "ntfs-3g.probe"; + +static const char *usage_msg = +"\n" +"%s %s - Probe NTFS volume mountability\n" +"\n" +"Copyright (C) 2007 Szabolcs Szakacsits\n" +"\n" +"Usage: %s <--readonly|--readwrite> <device|image_file>\n" +"\n" +"Example: ntfs-3g.probe --readwrite /dev/sda1\n" +"\n" +"%s"; + +static int ntfs_open(const char *device) +{ + ntfs_volume *vol; + unsigned long flags = 0; + int ret = NTFS_VOLUME_OK; + + if (opts.probetype == PROBE_READONLY) + flags |= NTFS_MNT_RDONLY; + + vol = ntfs_mount(device, flags); + if (!vol) + ret = ntfs_volume_error(errno); + + if (ret == 0 && ntfs_umount(vol, FALSE) == -1) + ret = ntfs_volume_error(errno); + + return ret; +} + +static void usage(void) +{ + ntfs_log_info(usage_msg, EXEC_NAME, VERSION, EXEC_NAME, ntfs_home); +} + +static int parse_options(int argc, char *argv[]) +{ + int c; + + static const char *sopt = "-hrw"; + static const struct option lopt[] = { + { "readonly", no_argument, NULL, 'r' }, + { "readwrite", no_argument, NULL, 'w' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + opterr = 0; /* We handle errors. */ + opts.probetype = PROBE_UNSET; + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!opts.device) { + opts.device = ntfs_malloc(PATH_MAX + 1); + if (!opts.device) + return -1; + + strncpy(opts.device, optarg, PATH_MAX); + opts.device[PATH_MAX] = 0; + } else { + ntfs_log_error("%s: You must specify exactly " + "one device\n", EXEC_NAME); + return -1; + } + break; + case 'h': + usage(); + exit(0); + case 'r': + opts.probetype = PROBE_READONLY; + break; + case 'w': + opts.probetype = PROBE_READWRITE; + break; + default: + ntfs_log_error("%s: Unknown option '%s'.\n", EXEC_NAME, + argv[optind - 1]); + return -1; + } + } + + if (!opts.device) { + ntfs_log_error("ERROR: %s: Device is missing\n", EXEC_NAME); + return -1; + } + + if (opts.probetype == PROBE_UNSET) { + ntfs_log_error("ERROR: %s: Probe type is missing\n", EXEC_NAME); + return -1; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + int err; + + ntfs_log_set_handler(ntfs_log_handler_stderr); + + if (parse_options(argc, argv)) { + usage(); + exit(NTFS_VOLUME_SYNTAX_ERROR); + } + + err = ntfs_open(opts.device); + + free(opts.device); + if (err) + exit(err); + return (0); +} + diff --git a/src/ntfs-3g.secaudit.8 b/src/ntfs-3g.secaudit.8 new file mode 100755 index 0000000000000000000000000000000000000000..669828e5432c1d7869768e5c95bec8aa81619eaa --- /dev/null +++ b/src/ntfs-3g.secaudit.8 @@ -0,0 +1,184 @@ +.\" Copyright (c) 2007-2009 Jean-Pierre André. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFS-3G.SECAUDIT 8 "February 2010" "ntfs-3g.secaudit 1.4.1" +.SH NAME +ntfs-3g.secaudit \- NTFS Security Data Auditing +.SH SYNOPSIS +.B ntfs-3g.secaudit +\fB[\fIoptions\fP\fB]\fR +.I args +.PP +Where \fIoptions\fP is a combination of : +.RS +-a full auditing of security data (Linux only) +.RE +.RS +-b backup ACLs +.RE +.RS +-e setting extra backed-up parameters (in conjunction with -s) +.RE +.RS +-h displaying hexadecimal security descriptors saved in a file +.RE +.RS +-r recursing in a directory +.RE +.RS +-s setting backed-up ACLs +.RE +.RS +-u getting a user mapping proposal +.RE +.RS +-v verbose (very verbose if set twice) +.RE +.PP +and args define the parameters and the set of files acted upon. +.PP +Typing secaudit with no args will display a summary of available options. +.SH DESCRIPTION +\fBntfs-3g.secaudit\fR +displays the ownership and permissions of a set of files on an NTFS +file system, and checks their consistency. It can be started in terminal +mode only (no graphical user interface is available.) +.PP +When a \fIvolume\fR is required, it has to be unmounted, and the command +has to be issued as \fBroot\fP. The \fIvolume\fR can be either a block +device (i.e. a disk partition) or an image file. +.PP +When acting on a directory or volume, the command may produce a lot +of information. It is therefore advisable to redirect the output to +a file or pipe it to a text editor for examination. +.SH OPTIONS +Below are the valid combinations of options and arguments that +\fBntfs-3g.secaudit\fR accepts. All the indicated arguments are +mandatory and must be unique (if wildcards are used, they must +resolve to a single name.) +.TP +\fB-h\fP \fIfile\fP +Displays in an human readable form the hexadecimal security descriptors +saved in \fIfile\fP. This can be used to turn a verbose output into a very +verbose output. +.TP +\fB-a[rv]\fP \fIvolume\fP +Audits the volume : all the global security data on \fIvolume\fP are scanned +and errors are displayed. If option \fB-r\fP is present, all files and +directories are also scanned and their relations to global security data +are checked. This can produce a lot of data. + +This option is not effective on volumes formatted for old NTFS versions (pre +NTFS 3.0). Such volumes have no global security data. + +When errors are signalled, it is advisable to repair the volume with an +appropriate tool (such as \fBchkdsk\fP on Windows.) +.TP +\fB[-v]\fP \fIvolume\fP \fIfile\fP +Displays the security parameters of \fIfile\fP : its interpreted Linux mode +(rwx flags in octal) and Posix ACL[1], its security key if any, and its +security descriptor if verbose output. +.TP +\fB-r[v]\fP \fIvolume\fP \fIdirectory\fP +displays the security parameters of all files and subdirectories in +\fIdirectory\fP : their interpreted Linux mode (rwx flags in octal) and Posix +ACL[1], their security key if any, and their security descriptor if +verbose output. +.TP +.B -b[v] \fIvolume\fP \fI[directory]\fP +Recursively extracts to standard output the NTFS ACLs of files in \fIvolume\fP +and \fIdirectory\fP. +.TP +\fB-s[ev]\fP \fIvolume\fP \fI[backup-file]\fP +Sets the NTFS ACLS as indicated in \fIbackup-file\fP or standard input. The +input data must have been created on Linux. With option \fB-e\fP, also sets +extra parameters (currently Windows attrib). +.TP +\fIvolume\fP \fIperms\fP \fIfile\fP +Sets the security parameters of file to perms. Perms is the Linux +requested mode (rwx flags, expressed in octal form as in chmod) or +a Posix ACL[1] (expressed like in setfacl -m). This sets a new ACL +which is effective for Linux and Windows. +.TP +\fB-r[v]\fP \fIvolume\fP \fIperms\fP \fIdirectory\fP +Sets the security parameters of all files and subdirectories in +\fIdirectory\fP to \fIperms\fP. Perms is the Linux requested mode (rwx flags, +expressed in octal form as in \fBchmod\fP), or a Posix ACL[1] (expressed like +in \fBsetfacl -m\fP.) This sets new ACLs which are effective for Linux and +Windows. +.TP +\fB[-v]\fP \fImounted-file\fP +Displays the security parameters of \fImounted-file\fP : its interpreted +Linux mode (rwx flags in octal) and Posix ACL[1], its security key if any, +and its security descriptor if verbose output. This is a special case which +acts on a mounted file (or directory) and does not require being root. The +Posix ACL interpretation can only be displayed if the full path to +\fImounted-file\fP from the root of the global file tree is provided. +.TP +\fB-u[v]\fP \fImounted-file\fP +Displays a proposed contents for a user mapping file, based on the +ownership parameters set by Windows on \fImounted-file\fP, assuming +this file was created on Windows by the user who should be mapped to the +current Linux user. The displayed information has to be copied to the +file \fB.NTFS-3G/UserMapping\fP where \fB.NTFS-3G\fP is a hidden +subdirectory of the root of the partition for which the mapping is to +be defined. This will cause the ownership of files created on that +partition to be the same as the original \fImounted-file\fP. +.SH NOTE +[1] provided the POSIX ACL option was selected at compile time. A Posix ACL +specification looks like "\fB[d:]{ugmo}:[id]:[perms],...\fP" where id is a +numeric user or group id, and perms an octal digit or a set from the letters +r, w and x. +.RS +Example : "\fBu::7,g::5,o:0,u:510:rwx,g:500:5,d:u:510:7\fP" +.SH EXAMPLES +Audit the global security data on /dev/sda1 +.RS +.sp +.B ntfs-3g.secaudit -ar /dev/sda1 +.sp +.RE +Display the ownership and permissions parameters for files in directory +/audio/music on device /dev/sda5, excluding sub-directories : +.RS +.sp +.B ntfs-3g.secaudit /dev/sda5 /audio/music +.sp +.RE +Set all files in directory /audio/music on device /dev/sda5 as writeable +by owner and read-only for everybody : +.RS +.sp +.B ntfs-3g.secaudit -r /dev/sda5 644 /audio/music +.sp +.RE +.SH EXIT CODES +.B ntfs-3g.secaudit +exits with a value of 0 when no error was detected, and with a value +of 1 when an error was detected. +.SH KNOWN ISSUES +Please see +.RS +.sp +http://www.tuxera.com/community/ntfs-3g-faq/ +.sp +.RE +for common questions and known issues. +If you would find a new one in the latest release of +the software then please send an email describing it +in detail. You can contact the +development team on the ntfs\-3g\-devel@lists.sf.net +address. +.SH AUTHORS +.B ntfs-3g.secaudit +has been developed by Jean-Pierre André. +.SH THANKS +Several people made heroic efforts, often over five or more +years which resulted the ntfs-3g driver. Most importantly they are +Anton Altaparmakov, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy, +Yuval Fledel, and the author of the groundbreaking FUSE filesystem development +framework, Miklos Szeredi. +.SH SEE ALSO +.BR ntfsprogs (8), +.BR attr (5), +.BR getfattr (1) diff --git a/src/ntfs-3g.secaudit.8.in b/src/ntfs-3g.secaudit.8.in new file mode 100755 index 0000000000000000000000000000000000000000..669828e5432c1d7869768e5c95bec8aa81619eaa --- /dev/null +++ b/src/ntfs-3g.secaudit.8.in @@ -0,0 +1,184 @@ +.\" Copyright (c) 2007-2009 Jean-Pierre André. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFS-3G.SECAUDIT 8 "February 2010" "ntfs-3g.secaudit 1.4.1" +.SH NAME +ntfs-3g.secaudit \- NTFS Security Data Auditing +.SH SYNOPSIS +.B ntfs-3g.secaudit +\fB[\fIoptions\fP\fB]\fR +.I args +.PP +Where \fIoptions\fP is a combination of : +.RS +-a full auditing of security data (Linux only) +.RE +.RS +-b backup ACLs +.RE +.RS +-e setting extra backed-up parameters (in conjunction with -s) +.RE +.RS +-h displaying hexadecimal security descriptors saved in a file +.RE +.RS +-r recursing in a directory +.RE +.RS +-s setting backed-up ACLs +.RE +.RS +-u getting a user mapping proposal +.RE +.RS +-v verbose (very verbose if set twice) +.RE +.PP +and args define the parameters and the set of files acted upon. +.PP +Typing secaudit with no args will display a summary of available options. +.SH DESCRIPTION +\fBntfs-3g.secaudit\fR +displays the ownership and permissions of a set of files on an NTFS +file system, and checks their consistency. It can be started in terminal +mode only (no graphical user interface is available.) +.PP +When a \fIvolume\fR is required, it has to be unmounted, and the command +has to be issued as \fBroot\fP. The \fIvolume\fR can be either a block +device (i.e. a disk partition) or an image file. +.PP +When acting on a directory or volume, the command may produce a lot +of information. It is therefore advisable to redirect the output to +a file or pipe it to a text editor for examination. +.SH OPTIONS +Below are the valid combinations of options and arguments that +\fBntfs-3g.secaudit\fR accepts. All the indicated arguments are +mandatory and must be unique (if wildcards are used, they must +resolve to a single name.) +.TP +\fB-h\fP \fIfile\fP +Displays in an human readable form the hexadecimal security descriptors +saved in \fIfile\fP. This can be used to turn a verbose output into a very +verbose output. +.TP +\fB-a[rv]\fP \fIvolume\fP +Audits the volume : all the global security data on \fIvolume\fP are scanned +and errors are displayed. If option \fB-r\fP is present, all files and +directories are also scanned and their relations to global security data +are checked. This can produce a lot of data. + +This option is not effective on volumes formatted for old NTFS versions (pre +NTFS 3.0). Such volumes have no global security data. + +When errors are signalled, it is advisable to repair the volume with an +appropriate tool (such as \fBchkdsk\fP on Windows.) +.TP +\fB[-v]\fP \fIvolume\fP \fIfile\fP +Displays the security parameters of \fIfile\fP : its interpreted Linux mode +(rwx flags in octal) and Posix ACL[1], its security key if any, and its +security descriptor if verbose output. +.TP +\fB-r[v]\fP \fIvolume\fP \fIdirectory\fP +displays the security parameters of all files and subdirectories in +\fIdirectory\fP : their interpreted Linux mode (rwx flags in octal) and Posix +ACL[1], their security key if any, and their security descriptor if +verbose output. +.TP +.B -b[v] \fIvolume\fP \fI[directory]\fP +Recursively extracts to standard output the NTFS ACLs of files in \fIvolume\fP +and \fIdirectory\fP. +.TP +\fB-s[ev]\fP \fIvolume\fP \fI[backup-file]\fP +Sets the NTFS ACLS as indicated in \fIbackup-file\fP or standard input. The +input data must have been created on Linux. With option \fB-e\fP, also sets +extra parameters (currently Windows attrib). +.TP +\fIvolume\fP \fIperms\fP \fIfile\fP +Sets the security parameters of file to perms. Perms is the Linux +requested mode (rwx flags, expressed in octal form as in chmod) or +a Posix ACL[1] (expressed like in setfacl -m). This sets a new ACL +which is effective for Linux and Windows. +.TP +\fB-r[v]\fP \fIvolume\fP \fIperms\fP \fIdirectory\fP +Sets the security parameters of all files and subdirectories in +\fIdirectory\fP to \fIperms\fP. Perms is the Linux requested mode (rwx flags, +expressed in octal form as in \fBchmod\fP), or a Posix ACL[1] (expressed like +in \fBsetfacl -m\fP.) This sets new ACLs which are effective for Linux and +Windows. +.TP +\fB[-v]\fP \fImounted-file\fP +Displays the security parameters of \fImounted-file\fP : its interpreted +Linux mode (rwx flags in octal) and Posix ACL[1], its security key if any, +and its security descriptor if verbose output. This is a special case which +acts on a mounted file (or directory) and does not require being root. The +Posix ACL interpretation can only be displayed if the full path to +\fImounted-file\fP from the root of the global file tree is provided. +.TP +\fB-u[v]\fP \fImounted-file\fP +Displays a proposed contents for a user mapping file, based on the +ownership parameters set by Windows on \fImounted-file\fP, assuming +this file was created on Windows by the user who should be mapped to the +current Linux user. The displayed information has to be copied to the +file \fB.NTFS-3G/UserMapping\fP where \fB.NTFS-3G\fP is a hidden +subdirectory of the root of the partition for which the mapping is to +be defined. This will cause the ownership of files created on that +partition to be the same as the original \fImounted-file\fP. +.SH NOTE +[1] provided the POSIX ACL option was selected at compile time. A Posix ACL +specification looks like "\fB[d:]{ugmo}:[id]:[perms],...\fP" where id is a +numeric user or group id, and perms an octal digit or a set from the letters +r, w and x. +.RS +Example : "\fBu::7,g::5,o:0,u:510:rwx,g:500:5,d:u:510:7\fP" +.SH EXAMPLES +Audit the global security data on /dev/sda1 +.RS +.sp +.B ntfs-3g.secaudit -ar /dev/sda1 +.sp +.RE +Display the ownership and permissions parameters for files in directory +/audio/music on device /dev/sda5, excluding sub-directories : +.RS +.sp +.B ntfs-3g.secaudit /dev/sda5 /audio/music +.sp +.RE +Set all files in directory /audio/music on device /dev/sda5 as writeable +by owner and read-only for everybody : +.RS +.sp +.B ntfs-3g.secaudit -r /dev/sda5 644 /audio/music +.sp +.RE +.SH EXIT CODES +.B ntfs-3g.secaudit +exits with a value of 0 when no error was detected, and with a value +of 1 when an error was detected. +.SH KNOWN ISSUES +Please see +.RS +.sp +http://www.tuxera.com/community/ntfs-3g-faq/ +.sp +.RE +for common questions and known issues. +If you would find a new one in the latest release of +the software then please send an email describing it +in detail. You can contact the +development team on the ntfs\-3g\-devel@lists.sf.net +address. +.SH AUTHORS +.B ntfs-3g.secaudit +has been developed by Jean-Pierre André. +.SH THANKS +Several people made heroic efforts, often over five or more +years which resulted the ntfs-3g driver. Most importantly they are +Anton Altaparmakov, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy, +Yuval Fledel, and the author of the groundbreaking FUSE filesystem development +framework, Miklos Szeredi. +.SH SEE ALSO +.BR ntfsprogs (8), +.BR attr (5), +.BR getfattr (1) diff --git a/src/ntfs-3g.usermap.8 b/src/ntfs-3g.usermap.8 new file mode 100755 index 0000000000000000000000000000000000000000..6efd47b9d1f31bc0b7d1159b547d144114d01d12 --- /dev/null +++ b/src/ntfs-3g.usermap.8 @@ -0,0 +1,96 @@ +.\" Copyright (c) 2007-2009 Jean-Pierre André. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFS-3G.USERMAP 8 "February 2010" "ntfs-3g.usermap 1.1.2" +.SH NAME +ntfs-3g.usermap \- NTFS Building a User Mapping File +.SH SYNOPSIS +.B ntfs-3g.usermap +\fIwindows-system-device\fP +\fB[\fIother-ntfs-device\fP...\fB]\fR +.PP +Where \fIwindows-system-device\fP is the device containing the Windows system +whose users are to be mapped to current Linux system. +.PP +And \fIother-ntfs-device\fP is another device containing files which are +to be accessed both by the Windows mentioned above and current Linux system. +.PP +the ntfs-3g.usermap command must be started as root, and the designated devices +must not be mounted. +.PP +Typing ntfs-3g.usermap with no args will display a summary of command +arguments. +.SH DESCRIPTION +\fBntfs-3g.usermap\fR +creates the file defining the mapping of Windows accounts to Linux logins for +users who owns files which should be visible from both Windows and +Linux. +.PP +It relies on existing files which were created on Windows, trying +to locate significant files and asking which Linux user or group should +own them. When a Linux owner or group is requested, the reply may be : +.PP +- the uid or gid (numeric or symbolic) of Linux owner or group of the file. +.RS +In that situation, no more file with the same Windows owner will be selected. +.RE +- or no answer, when not able to define the owner or group. +.RS +In that situation another file owned by the same Windows user or group +may be selected later so that a mapping can be defined. +.RE +.PP +The mappings for standard Windows users, such as "Administrator" or +"All Users" are defined implicitly. As a consequence a user mapping should +never be defined as Linux root. +.PP +When there are no more significant files, ntfs-3g.usermap create the +mapping file into the file UserMapping in the current directory. This +file has to be moved to the hidden directory .NTFS-3G in the root of +all the NTFS file systems to be shared between Windows and Linux. This +requires the file system to be mounted, but the created file will not +be taken into account if not present at mount time, which means the +file system has to be unmounted and mounted again for the new mapping +file to be taken into account. +.SH OPTIONS +No option is defined for ntfs-3g.usermap. +.SH EXAMPLES +Map the users defined on the Windows system present on /dev/sda1 : +.RS +.sp +.B ntfs-3g.usermap /dev/sda1 +.sp +.RE +.PP +A detailed example, with screen displays is available on +http://pagesperso-orange.fr/b.andre/usermap.html +.SH EXIT CODES +.B ntfs-3g.usermap +exits with a value of 0 when no error was detected, and with a value +of 1 when an error was detected. +.SH KNOWN ISSUES +Please see +.RS +.sp +http://www.tuxera.com/community/ntfs-3g-faq/ +.sp +.RE +for common questions and known issues. +If you would find a new one in the latest release of +the software then please send an email describing it +in detail. You can contact the +development team on the ntfs\-3g\-devel@lists.sf.net +address. +.SH AUTHORS +.B ntfs-3g.secaudit +has been developed by Jean-Pierre André. +.SH THANKS +Several people made heroic efforts, often over five or more +years which resulted the ntfs-3g driver. Most importantly they are +Anton Altaparmakov, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy, +Yuval Fledel, and the author of the groundbreaking FUSE filesystem development +framework, Miklos Szeredi. +.SH SEE ALSO +.BR ntfsprogs (8), +.BR attr (5), +.BR getfattr (1) diff --git a/src/ntfs-3g.usermap.8.in b/src/ntfs-3g.usermap.8.in new file mode 100755 index 0000000000000000000000000000000000000000..6efd47b9d1f31bc0b7d1159b547d144114d01d12 --- /dev/null +++ b/src/ntfs-3g.usermap.8.in @@ -0,0 +1,96 @@ +.\" Copyright (c) 2007-2009 Jean-Pierre André. +.\" This file may be copied under the terms of the GNU Public License. +.\" +.TH NTFS-3G.USERMAP 8 "February 2010" "ntfs-3g.usermap 1.1.2" +.SH NAME +ntfs-3g.usermap \- NTFS Building a User Mapping File +.SH SYNOPSIS +.B ntfs-3g.usermap +\fIwindows-system-device\fP +\fB[\fIother-ntfs-device\fP...\fB]\fR +.PP +Where \fIwindows-system-device\fP is the device containing the Windows system +whose users are to be mapped to current Linux system. +.PP +And \fIother-ntfs-device\fP is another device containing files which are +to be accessed both by the Windows mentioned above and current Linux system. +.PP +the ntfs-3g.usermap command must be started as root, and the designated devices +must not be mounted. +.PP +Typing ntfs-3g.usermap with no args will display a summary of command +arguments. +.SH DESCRIPTION +\fBntfs-3g.usermap\fR +creates the file defining the mapping of Windows accounts to Linux logins for +users who owns files which should be visible from both Windows and +Linux. +.PP +It relies on existing files which were created on Windows, trying +to locate significant files and asking which Linux user or group should +own them. When a Linux owner or group is requested, the reply may be : +.PP +- the uid or gid (numeric or symbolic) of Linux owner or group of the file. +.RS +In that situation, no more file with the same Windows owner will be selected. +.RE +- or no answer, when not able to define the owner or group. +.RS +In that situation another file owned by the same Windows user or group +may be selected later so that a mapping can be defined. +.RE +.PP +The mappings for standard Windows users, such as "Administrator" or +"All Users" are defined implicitly. As a consequence a user mapping should +never be defined as Linux root. +.PP +When there are no more significant files, ntfs-3g.usermap create the +mapping file into the file UserMapping in the current directory. This +file has to be moved to the hidden directory .NTFS-3G in the root of +all the NTFS file systems to be shared between Windows and Linux. This +requires the file system to be mounted, but the created file will not +be taken into account if not present at mount time, which means the +file system has to be unmounted and mounted again for the new mapping +file to be taken into account. +.SH OPTIONS +No option is defined for ntfs-3g.usermap. +.SH EXAMPLES +Map the users defined on the Windows system present on /dev/sda1 : +.RS +.sp +.B ntfs-3g.usermap /dev/sda1 +.sp +.RE +.PP +A detailed example, with screen displays is available on +http://pagesperso-orange.fr/b.andre/usermap.html +.SH EXIT CODES +.B ntfs-3g.usermap +exits with a value of 0 when no error was detected, and with a value +of 1 when an error was detected. +.SH KNOWN ISSUES +Please see +.RS +.sp +http://www.tuxera.com/community/ntfs-3g-faq/ +.sp +.RE +for common questions and known issues. +If you would find a new one in the latest release of +the software then please send an email describing it +in detail. You can contact the +development team on the ntfs\-3g\-devel@lists.sf.net +address. +.SH AUTHORS +.B ntfs-3g.secaudit +has been developed by Jean-Pierre André. +.SH THANKS +Several people made heroic efforts, often over five or more +years which resulted the ntfs-3g driver. Most importantly they are +Anton Altaparmakov, Richard Russon, Szabolcs Szakacsits, Yura Pakhuchiy, +Yuval Fledel, and the author of the groundbreaking FUSE filesystem development +framework, Miklos Szeredi. +.SH SEE ALSO +.BR ntfsprogs (8), +.BR attr (5), +.BR getfattr (1) diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c new file mode 100755 index 0000000000000000000000000000000000000000..d6e2942d8b48ab298382afe1a0dca54ed1c2fb6e --- /dev/null +++ b/src/ntfs-3g_common.c @@ -0,0 +1,762 @@ +/** + * ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g. + * + * Copyright (c) 2010-2015 Jean-Pierre Andre + * Copyright (c) 2010 Erik Larsson + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include <getopt.h> +#include <fuse.h> + +#include "inode.h" +#include "security.h" +#include "xattrs.h" +#include "ntfs-3g_common.h" +#include "realpath.h" +#include "misc.h" + +const char xattr_ntfs_3g[] = "ntfs-3g."; + +const char nf_ns_user_prefix[] = "user."; +const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1; +const char nf_ns_system_prefix[] = "system."; +const int nf_ns_system_prefix_len = sizeof(nf_ns_system_prefix) - 1; +const char nf_ns_security_prefix[] = "security."; +const int nf_ns_security_prefix_len = sizeof(nf_ns_security_prefix) - 1; +const char nf_ns_trusted_prefix[] = "trusted."; +const int nf_ns_trusted_prefix_len = sizeof(nf_ns_trusted_prefix) - 1; + +static const char nf_ns_alt_xattr_efsinfo[] = "user.ntfs.efsinfo"; + +static const char def_opts[] = "allow_other,nonempty,"; + + /* + * Table of recognized options + * Their order may be significant + * The options invalid in some configuration should still + * be present, so that an error can be returned + */ +const struct DEFOPTION optionlist[] = { + { "ro", OPT_RO, FLGOPT_APPEND | FLGOPT_BOGUS }, + { "noatime", OPT_NOATIME, FLGOPT_BOGUS }, + { "atime", OPT_ATIME, FLGOPT_BOGUS }, + { "relatime", OPT_RELATIME, FLGOPT_BOGUS }, + { "delay_mtime", OPT_DMTIME, FLGOPT_DECIMAL | FLGOPT_OPTIONAL }, + { "fake_rw", OPT_FAKE_RW, FLGOPT_BOGUS }, + { "fsname", OPT_FSNAME, FLGOPT_NOSUPPORT }, + { "no_def_opts", OPT_NO_DEF_OPTS, FLGOPT_BOGUS }, + { "default_permissions", OPT_DEFAULT_PERMISSIONS, FLGOPT_BOGUS }, + { "permissions", OPT_PERMISSIONS, FLGOPT_BOGUS }, + { "acl", OPT_ACL, FLGOPT_BOGUS }, + { "umask", OPT_UMASK, FLGOPT_OCTAL }, + { "fmask", OPT_FMASK, FLGOPT_OCTAL }, + { "dmask", OPT_DMASK, FLGOPT_OCTAL }, + { "uid", OPT_UID, FLGOPT_DECIMAL }, + { "gid", OPT_GID, FLGOPT_DECIMAL }, + { "show_sys_files", OPT_SHOW_SYS_FILES, FLGOPT_BOGUS }, + { "hide_hid_files", OPT_HIDE_HID_FILES, FLGOPT_BOGUS }, + { "hide_dot_files", OPT_HIDE_DOT_FILES, FLGOPT_BOGUS }, + { "ignore_case", OPT_IGNORE_CASE, FLGOPT_BOGUS }, + { "windows_names", OPT_WINDOWS_NAMES, FLGOPT_BOGUS }, + { "compression", OPT_COMPRESSION, FLGOPT_BOGUS }, + { "nocompression", OPT_NOCOMPRESSION, FLGOPT_BOGUS }, + { "silent", OPT_SILENT, FLGOPT_BOGUS }, + { "recover", OPT_RECOVER, FLGOPT_BOGUS }, + { "norecover", OPT_NORECOVER, FLGOPT_BOGUS }, + { "remove_hiberfile", OPT_REMOVE_HIBERFILE, FLGOPT_BOGUS }, + { "sync", OPT_SYNC, FLGOPT_BOGUS | FLGOPT_APPEND }, + { "big_writes", OPT_BIG_WRITES, FLGOPT_BOGUS }, + { "locale", OPT_LOCALE, FLGOPT_STRING }, + { "nfconv", OPT_NFCONV, FLGOPT_BOGUS }, + { "nonfconv", OPT_NONFCONV, FLGOPT_BOGUS }, + { "streams_interface", OPT_STREAMS_INTERFACE, FLGOPT_STRING }, + { "user_xattr", OPT_USER_XATTR, FLGOPT_BOGUS }, + { "noauto", OPT_NOAUTO, FLGOPT_BOGUS }, + { "debug", OPT_DEBUG, FLGOPT_BOGUS }, + { "no_detach", OPT_NO_DETACH, FLGOPT_BOGUS }, + { "remount", OPT_REMOUNT, FLGOPT_BOGUS }, + { "blksize", OPT_BLKSIZE, FLGOPT_STRING }, + { "inherit", OPT_INHERIT, FLGOPT_BOGUS }, + { "addsecurids", OPT_ADDSECURIDS, FLGOPT_BOGUS }, + { "staticgrps", OPT_STATICGRPS, FLGOPT_BOGUS }, + { "usermapping", OPT_USERMAPPING, FLGOPT_STRING }, + { "xattrmapping", OPT_XATTRMAPPING, FLGOPT_STRING }, + { "efs_raw", OPT_EFS_RAW, FLGOPT_BOGUS }, + { (const char*)NULL, 0, 0 } /* end marker */ +} ; + +#define STRAPPEND_MAX_INSIZE 8192 +#define strappend_is_large(x) ((x) > STRAPPEND_MAX_INSIZE) + +int ntfs_strappend(char **dest, const char *append) +{ + char *p; + size_t size_append, size_dest = 0; + + if (!dest) + return -1; + if (!append) + return 0; + + size_append = strlen(append); + if (*dest) + size_dest = strlen(*dest); + + if (strappend_is_large(size_dest) || strappend_is_large(size_append)) { + errno = EOVERFLOW; + ntfs_log_perror("%s: Too large input buffer", EXEC_NAME); + return -1; + } + + p = (char*)realloc(*dest, size_dest + size_append + 1); + if (!p) { + ntfs_log_perror("%s: Memory realloction failed", EXEC_NAME); + return -1; + } + + *dest = p; + strcpy(*dest + size_dest, append); + + return 0; +} + +/* + * Insert an option before ",fsname=" + * This is for keeping "fsname" as the last option, because on + * Solaris device names may contain commas. + */ + +int ntfs_strinsert(char **dest, const char *append) +{ + char *p, *q; + size_t size_append, size_dest = 0; + + if (!dest) + return -1; + if (!append) + return 0; + + size_append = strlen(append); + if (*dest) + size_dest = strlen(*dest); + + if (strappend_is_large(size_dest) || strappend_is_large(size_append)) { + errno = EOVERFLOW; + ntfs_log_perror("%s: Too large input buffer", EXEC_NAME); + return -1; + } + + p = (char*)malloc(size_dest + size_append + 1); + if (!p) { + ntfs_log_perror("%s: Memory reallocation failed", EXEC_NAME); + return -1; + } + strcpy(p, *dest); + q = strstr(p, ",fsname="); + if (q) { + strcpy(q, append); + q = strstr(*dest, ",fsname="); + if (q) + strcat(p, q); + free(*dest); + *dest = p; + } else { + free(*dest); + *dest = p; + strcpy(*dest + size_dest, append); + } + return 0; +} + +static int bogus_option_value(char *val, const char *s) +{ + if (val) { + ntfs_log_error("'%s' option shouldn't have value.\n", s); + return -1; + } + return 0; +} + +static int missing_option_value(char *val, const char *s) +{ + if (!val) { + ntfs_log_error("'%s' option should have a value.\n", s); + return -1; + } + return 0; +} + +char *parse_mount_options(ntfs_fuse_context_t *ctx, + const struct ntfs_options *popts, BOOL low_fuse) +{ + char *options, *s, *opt, *val, *ret = NULL; + char *sz_device = NULL; + const char *orig_opts = popts->options; + BOOL no_def_opts = FALSE; + int default_permissions = 0; + int permissions = 0; + int acl = 0; + int want_permissions = 0; + int intarg; + const struct DEFOPTION *poptl; + + ctx->secure_flags = 0; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + ctx->efs_raw = FALSE; +#endif /* HAVE_SETXATTR */ + ctx->compression = DEFAULT_COMPRESSION; + options = strdup(orig_opts ? orig_opts : ""); + if (!options) { + ntfs_log_perror("%s: strdup failed", EXEC_NAME); + return NULL; + } + + s = options; + while (s && *s && (val = strsep(&s, ","))) { + opt = strsep(&val, "="); + poptl = optionlist; + while (poptl->name && strcmp(poptl->name,opt)) + poptl++; + if (poptl->name) { + if ((poptl->flags & FLGOPT_BOGUS) + && bogus_option_value(val, opt)) + goto err_exit; + if ((poptl->flags & FLGOPT_OCTAL) + && (!val + || !sscanf(val, "%o", &intarg))) { + ntfs_log_error("'%s' option needs an octal value\n", + opt); + goto err_exit; + } + if (poptl->flags & FLGOPT_DECIMAL) { + if ((poptl->flags & FLGOPT_OPTIONAL) && !val) + intarg = 0; + else + if (!val + || !sscanf(val, "%i", &intarg)) { + ntfs_log_error("'%s' option " + "needs a decimal value\n", + opt); + goto err_exit; + } + } + if ((poptl->flags & FLGOPT_STRING) + && missing_option_value(val, opt)) + goto err_exit; + + switch (poptl->type) { + case OPT_RO : + case OPT_FAKE_RW : + ctx->ro = TRUE; + break; + case OPT_NOATIME : + ctx->atime = ATIME_DISABLED; + break; + case OPT_ATIME : + ctx->atime = ATIME_ENABLED; + break; + case OPT_RELATIME : + ctx->atime = ATIME_RELATIVE; + break; + case OPT_DMTIME : + if (!intarg) + intarg = DEFAULT_DMTIME; + ctx->dmtime = intarg*10000000LL; + break; + case OPT_NO_DEF_OPTS : + no_def_opts = TRUE; /* Don't add default options. */ + ctx->silent = FALSE; /* cancel default silent */ + break; + case OPT_DEFAULT_PERMISSIONS : + default_permissions = 1; + break; + case OPT_PERMISSIONS : + permissions = 1; + break; +#if POSIXACLS + case OPT_ACL : + acl = 1; + break; +#endif + case OPT_UMASK : + ctx->dmask = ctx->fmask = intarg; + want_permissions = 1; + break; + case OPT_FMASK : + ctx->fmask = intarg; + want_permissions = 1; + break; + case OPT_DMASK : + ctx->dmask = intarg; + want_permissions = 1; + break; + case OPT_UID : + ctx->uid = intarg; + want_permissions = 1; + break; + case OPT_GID : + ctx->gid = intarg; + want_permissions = 1; + break; + case OPT_SHOW_SYS_FILES : + ctx->show_sys_files = TRUE; + break; + case OPT_HIDE_HID_FILES : + ctx->hide_hid_files = TRUE; + break; + case OPT_HIDE_DOT_FILES : + ctx->hide_dot_files = TRUE; + break; + case OPT_WINDOWS_NAMES : + ctx->windows_names = TRUE; + break; + case OPT_IGNORE_CASE : + if (low_fuse) + ctx->ignore_case = TRUE; + else { + ntfs_log_error("'%s' is an unsupported option.\n", + poptl->name); + goto err_exit; + } + break; + case OPT_COMPRESSION : + ctx->compression = TRUE; + break; + case OPT_NOCOMPRESSION : + ctx->compression = FALSE; + break; + case OPT_SILENT : + ctx->silent = TRUE; + break; + case OPT_RECOVER : + ctx->recover = TRUE; + break; + case OPT_NORECOVER : + ctx->recover = FALSE; + break; + case OPT_REMOVE_HIBERFILE : + ctx->hiberfile = TRUE; + break; + case OPT_SYNC : + ctx->sync = TRUE; + break; +#ifdef FUSE_CAP_BIG_WRITES + case OPT_BIG_WRITES : + ctx->big_writes = TRUE; + break; +#endif + case OPT_LOCALE : + ntfs_set_char_encoding(val); + break; +#if defined(__APPLE__) || defined(__DARWIN__) +#ifdef ENABLE_NFCONV + case OPT_NFCONV : + if (ntfs_macosx_normalize_filenames(1)) { + ntfs_log_error("ntfs_macosx_normalize_filenames(1) failed!\n"); + goto err_exit; + } + break; + case OPT_NONFCONV : + if (ntfs_macosx_normalize_filenames(0)) { + ntfs_log_error("ntfs_macosx_normalize_filenames(0) failed!\n"); + goto err_exit; + } + break; +#endif /* ENABLE_NFCONV */ +#endif /* defined(__APPLE__) || defined(__DARWIN__) */ + case OPT_STREAMS_INTERFACE : + if (!strcmp(val, "none")) + ctx->streams = NF_STREAMS_INTERFACE_NONE; + else if (!strcmp(val, "xattr")) + ctx->streams = NF_STREAMS_INTERFACE_XATTR; + else if (!strcmp(val, "openxattr")) + ctx->streams = NF_STREAMS_INTERFACE_OPENXATTR; + else if (!low_fuse && !strcmp(val, "windows")) + ctx->streams = NF_STREAMS_INTERFACE_WINDOWS; + else { + ntfs_log_error("Invalid named data streams " + "access interface.\n"); + goto err_exit; + } + break; + case OPT_USER_XATTR : + ctx->streams = NF_STREAMS_INTERFACE_XATTR; + break; + case OPT_NOAUTO : + /* Don't pass noauto option to fuse. */ + break; + case OPT_DEBUG : + ctx->debug = TRUE; + ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); + ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); + break; + case OPT_NO_DETACH : + ctx->no_detach = TRUE; + break; + case OPT_REMOUNT : + ntfs_log_error("Remounting is not supported at present." + " You have to umount volume and then " + "mount it once again.\n"); + goto err_exit; + case OPT_BLKSIZE : + ntfs_log_info("WARNING: blksize option is ignored " + "because ntfs-3g must calculate it.\n"); + break; + case OPT_INHERIT : + /* + * do not overwrite inherited permissions + * in create() + */ + ctx->inherit = TRUE; + break; + case OPT_ADDSECURIDS : + /* + * create security ids for files being read + * with an individual security attribute + */ + ctx->secure_flags |= (1 << SECURITY_ADDSECURIDS); + break; + case OPT_STATICGRPS : + /* + * use static definition of groups + * for file access control + */ + ctx->secure_flags |= (1 << SECURITY_STATICGRPS); + break; + case OPT_USERMAPPING : + ctx->usermap_path = strdup(val); + if (!ctx->usermap_path) { + ntfs_log_error("no more memory to store " + "'usermapping' option.\n"); + goto err_exit; + } + break; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ +#ifdef XATTR_MAPPINGS + case OPT_XATTRMAPPING : + ctx->xattrmap_path = strdup(val); + if (!ctx->xattrmap_path) { + ntfs_log_error("no more memory to store " + "'xattrmapping' option.\n"); + goto err_exit; + } + break; +#endif /* XATTR_MAPPINGS */ + case OPT_EFS_RAW : + ctx->efs_raw = TRUE; + break; +#endif /* HAVE_SETXATTR */ + case OPT_FSNAME : /* Filesystem name. */ + /* + * We need this to be able to check whether filesystem + * mounted or not. + * (falling through to default) + */ + default : + ntfs_log_error("'%s' is an unsupported option.\n", + poptl->name); + goto err_exit; + } + if ((poptl->flags & FLGOPT_APPEND) + && (ntfs_strappend(&ret, poptl->name) + || ntfs_strappend(&ret, ","))) + goto err_exit; + } else { /* Probably FUSE option. */ + if (ntfs_strappend(&ret, opt)) + goto err_exit; + if (val) { + if (ntfs_strappend(&ret, "=")) + goto err_exit; + if (ntfs_strappend(&ret, val)) + goto err_exit; + } + if (ntfs_strappend(&ret, ",")) + goto err_exit; + } + } + if (!no_def_opts && ntfs_strappend(&ret, def_opts)) + goto err_exit; + if ((default_permissions || (permissions && !acl)) + && ntfs_strappend(&ret, "default_permissions,")) + goto err_exit; + /* The atime options exclude each other */ + if (ctx->atime == ATIME_RELATIVE && ntfs_strappend(&ret, "relatime,")) + goto err_exit; + else if (ctx->atime == ATIME_ENABLED && ntfs_strappend(&ret, "atime,")) + goto err_exit; + else if (ctx->atime == ATIME_DISABLED && ntfs_strappend(&ret, "noatime,")) + goto err_exit; + + if (ntfs_strappend(&ret, "fsname=")) + goto err_exit; + sz_device = malloc(strlen(popts->device) + 1); + if (!sz_device) + goto err_exit; + strcpy(sz_device, popts->device); + char *r_device = strchr(sz_device, ','); + if (r_device) + r_device[0] = '@'; + if (ntfs_strappend(&ret, sz_device)) + goto err_exit; + if (permissions && !acl) + ctx->secure_flags |= (1 << SECURITY_DEFAULT); + if (acl) + ctx->secure_flags |= (1 << SECURITY_ACL); + if (want_permissions) + ctx->secure_flags |= (1 << SECURITY_WANTED); + if (ctx->ro) { + ctx->secure_flags &= ~(1 << SECURITY_ADDSECURIDS); + ctx->hiberfile = FALSE; + } +exit: + if (sz_device) + free(sz_device); + free(options); + return ret; +err_exit: + free(ret); + ret = NULL; + goto exit; +} + +/** + * parse_options - Read and validate the programs command line + * Read the command line, verify the syntax and parse the options. + * + * Return: 0 success, -1 error. + */ +int ntfs_parse_options(struct ntfs_options *popts, void (*usage)(void), + int argc, char *argv[]) +{ + int c; + + static const char *sopt = "-o:hnsvV"; + static const struct option lopt[] = { + { "options", required_argument, NULL, 'o' }, + { "help", no_argument, NULL, 'h' }, + { "no-mtab", no_argument, NULL, 'n' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + opterr = 0; /* We'll handle the errors, thank you. */ + + while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (c) { + case 1: /* A non-option argument */ + if (!popts->device) { + popts->device = ntfs_malloc(PATH_MAX + 1); + if (!popts->device) + return -1; + + /* Canonicalize device name (mtab, etc) */ + popts->arg_device = optarg; + if (!ntfs_realpath_canonicalize(optarg, + popts->device)) { + ntfs_log_perror("%s: Failed to access " + "volume '%s'", EXEC_NAME, optarg); + free(popts->device); + popts->device = NULL; + return -1; + } + } else if (!popts->mnt_point) { + popts->mnt_point = optarg; + } else { + ntfs_log_error("%s: You must specify exactly one " + "device and exactly one mount " + "point.\n", EXEC_NAME); + return -1; + } + break; + case 'o': + if (popts->options) + if (ntfs_strappend(&popts->options, ",")) + return -1; + if (ntfs_strappend(&popts->options, optarg)) + return -1; + break; + case 'h': + usage(); + exit(9); + case 'n': + /* + * no effect - automount passes it, meaning 'no-mtab' + */ + break; + case 's': + /* + * no effect - automount passes it, meaning sloppy + */ + break; + case 'v': + /* + * We must handle the 'verbose' option even if + * we don't use it because mount(8) passes it. + */ + break; + case 'V': + ntfs_log_info("%s %s %s %d\n", EXEC_NAME, VERSION, + FUSE_TYPE, fuse_version()); + exit(0); + default: + ntfs_log_error("%s: Unknown option '%s'.\n", EXEC_NAME, + argv[optind - 1]); + return -1; + } + } + + if (!popts->device) { + ntfs_log_error("%s: No device is specified.\n", EXEC_NAME); + return -1; + } + if (!popts->mnt_point) { + ntfs_log_error("%s: No mountpoint is specified.\n", EXEC_NAME); + return -1; + } + + return 0; +} + +#ifdef HAVE_SETXATTR + +int ntfs_fuse_listxattr_common(ntfs_inode *ni, ntfs_attr_search_ctx *actx, + char *list, size_t size, BOOL prefixing) +{ + int ret = 0; + char *to = list; +#ifdef XATTR_MAPPINGS + BOOL accepted; + const struct XATTRMAPPING *item; +#endif /* XATTR_MAPPINGS */ + + /* first list the regular user attributes (ADS) */ + while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, + 0, NULL, 0, actx)) { + char *tmp_name = NULL; + int tmp_name_len; + + if (!actx->attr->name_length) + continue; + tmp_name_len = ntfs_ucstombs( + (ntfschar *)((u8*)actx->attr + + le16_to_cpu(actx->attr->name_offset)), + actx->attr->name_length, &tmp_name, 0); + if (tmp_name_len < 0) { + ret = -errno; + goto exit; + } + /* + * When using name spaces, do not return + * security, trusted or system attributes + * (filtered elsewhere anyway) + * otherwise insert "user." prefix + */ + if (prefixing) { + if ((strlen(tmp_name) > sizeof(xattr_ntfs_3g)) + && !strncmp(tmp_name,xattr_ntfs_3g, + sizeof(xattr_ntfs_3g)-1)) + tmp_name_len = 0; + else + ret += tmp_name_len + + nf_ns_user_prefix_len + 1; + } else + ret += tmp_name_len + 1; + if (size && tmp_name_len) { + if ((size_t)ret <= size) { + if (prefixing) { + strcpy(to, nf_ns_user_prefix); + to += nf_ns_user_prefix_len; + } + strncpy(to, tmp_name, tmp_name_len); + to += tmp_name_len; + *to = 0; + to++; + } else { + free(tmp_name); + ret = -ERANGE; + goto exit; + } + } + free(tmp_name); + } +#ifdef XATTR_MAPPINGS + /* now append the system attributes mapped to user space */ + for (item=ni->vol->xattr_mapping; item; item=item->next) { + switch (item->xattr) { + case XATTR_NTFS_EFSINFO : + accepted = ni->vol->efs_raw + && (ni->flags & FILE_ATTR_ENCRYPTED); + break; + case XATTR_NTFS_REPARSE_DATA : + accepted = (ni->flags & FILE_ATTR_REPARSE_POINT) + != const_cpu_to_le32(0); + break; +// TODO : we are supposed to only return xattrs which are set +// this is more complex for OBJECT_ID and DOS_NAME + default : accepted = TRUE; + break; + } + if (accepted) { + ret += strlen(item->name) + 1; + if (size) { + if ((size_t)ret <= size) { + strcpy(to, item->name); + to += strlen(item->name); + *to++ = 0; + } else { + ret = -ERANGE; + goto exit; + } + } +#else /* XATTR_MAPPINGS */ + /* List efs info xattr for encrypted files */ + if (ni->vol->efs_raw && (ni->flags & FILE_ATTR_ENCRYPTED)) { + ret += sizeof(nf_ns_alt_xattr_efsinfo); + if ((size_t)ret <= size) { + memcpy(to, nf_ns_alt_xattr_efsinfo, + sizeof(nf_ns_alt_xattr_efsinfo)); + to += sizeof(nf_ns_alt_xattr_efsinfo); +#endif /* XATTR_MAPPINGS */ + } + } +exit : + return (ret); +} + +#endif /* HAVE_SETXATTR */ diff --git a/src/ntfs-3g_common.h b/src/ntfs-3g_common.h new file mode 100755 index 0000000000000000000000000000000000000000..e68c699226d8f0a873ab3629499754b042c0e885 --- /dev/null +++ b/src/ntfs-3g_common.h @@ -0,0 +1,185 @@ +/* + * ntfs-3g_common.h - Common declarations for ntfs-3g and lowntfs-3g. + * + * Copyright (c) 2010-2011 Jean-Pierre Andre + * Copyright (c) 2010 Erik Larsson + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_3G_COMMON_H +#define _NTFS_3G_COMMON_H + +#include "inode.h" + +struct ntfs_options { + char *mnt_point; /* Mount point */ + char *options; /* Mount options */ + char *device; /* Device to mount */ + char *arg_device; /* Device requested in argv */ +} ; + +typedef enum { + NF_STREAMS_INTERFACE_NONE, /* No access to named data streams. */ + NF_STREAMS_INTERFACE_XATTR, /* Map named data streams to xattrs. */ + NF_STREAMS_INTERFACE_OPENXATTR, /* Same, not limited to "user." */ + NF_STREAMS_INTERFACE_WINDOWS, /* "file:stream" interface. */ +} ntfs_fuse_streams_interface; + +struct DEFOPTION { + const char *name; + int type; + int flags; +} ; + /* Options, order not significant */ +enum { + OPT_RO, + OPT_NOATIME, + OPT_ATIME, + OPT_RELATIME, + OPT_DMTIME, + OPT_FAKE_RW, + OPT_FSNAME, + OPT_NO_DEF_OPTS, + OPT_DEFAULT_PERMISSIONS, + OPT_PERMISSIONS, + OPT_ACL, + OPT_UMASK, + OPT_FMASK, + OPT_DMASK, + OPT_UID, + OPT_GID, + OPT_SHOW_SYS_FILES, + OPT_HIDE_HID_FILES, + OPT_HIDE_DOT_FILES, + OPT_IGNORE_CASE, + OPT_WINDOWS_NAMES, + OPT_COMPRESSION, + OPT_NOCOMPRESSION, + OPT_SILENT, + OPT_RECOVER, + OPT_NORECOVER, + OPT_REMOVE_HIBERFILE, + OPT_SYNC, + OPT_BIG_WRITES, + OPT_LOCALE, + OPT_NFCONV, + OPT_NONFCONV, + OPT_STREAMS_INTERFACE, + OPT_USER_XATTR, + OPT_NOAUTO, + OPT_DEBUG, + OPT_NO_DETACH, + OPT_REMOUNT, + OPT_BLKSIZE, + OPT_INHERIT, + OPT_ADDSECURIDS, + OPT_STATICGRPS, + OPT_USERMAPPING, + OPT_XATTRMAPPING, + OPT_EFS_RAW, +} ; + + /* Option flags */ +enum { + FLGOPT_BOGUS = 1, + FLGOPT_STRING = 2, + FLGOPT_OCTAL = 4, + FLGOPT_DECIMAL = 8, + FLGOPT_APPEND = 16, + FLGOPT_NOSUPPORT = 32, + FLGOPT_OPTIONAL = 64 +} ; + +typedef enum { + ATIME_ENABLED, + ATIME_DISABLED, + ATIME_RELATIVE +} ntfs_atime_t; + +typedef struct { + ntfs_volume *vol; + unsigned int uid; + unsigned int gid; + unsigned int fmask; + unsigned int dmask; + ntfs_fuse_streams_interface streams; + ntfs_atime_t atime; + u64 dmtime; + BOOL ro; + BOOL show_sys_files; + BOOL hide_hid_files; + BOOL hide_dot_files; + BOOL windows_names; + BOOL ignore_case; + BOOL compression; + BOOL acl; + BOOL silent; + BOOL recover; + BOOL hiberfile; + BOOL sync; + BOOL big_writes; + BOOL debug; + BOOL no_detach; + BOOL blkdev; + BOOL mounted; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + BOOL efs_raw; +#ifdef XATTR_MAPPINGS + char *xattrmap_path; +#endif /* XATTR_MAPPINGS */ +#endif /* HAVE_SETXATTR */ + struct fuse_chan *fc; + BOOL inherit; + unsigned int secure_flags; + char *usermap_path; + char *abs_mnt_point; + struct PERMISSIONS_CACHE *seccache; + struct SECURITY_CONTEXT security; + struct open_file *open_files; /* only defined in lowntfs-3g */ + u64 latest_ghost; +} ntfs_fuse_context_t; + +extern const char *EXEC_NAME; + +#ifdef FUSE_INTERNAL +#define FUSE_TYPE "integrated FUSE" +#else +#define FUSE_TYPE "external FUSE" +#endif + +extern const char xattr_ntfs_3g[]; + +extern const char nf_ns_user_prefix[]; +extern const int nf_ns_user_prefix_len; +extern const char nf_ns_system_prefix[]; +extern const int nf_ns_system_prefix_len; +extern const char nf_ns_security_prefix[]; +extern const int nf_ns_security_prefix_len; +extern const char nf_ns_trusted_prefix[]; +extern const int nf_ns_trusted_prefix_len; + +int ntfs_strappend(char **dest, const char *append); +int ntfs_strinsert(char **dest, const char *append); +char *parse_mount_options(ntfs_fuse_context_t *ctx, + const struct ntfs_options *popts, BOOL low_fuse); +int ntfs_parse_options(struct ntfs_options *popts, void (*usage)(void), + int argc, char *argv[]); + +int ntfs_fuse_listxattr_common(ntfs_inode *ni, ntfs_attr_search_ctx *actx, + char *list, size_t size, BOOL prefixing); + +#endif /* _NTFS_3G_COMMON_H */ diff --git a/src/secaudit.c b/src/secaudit.c new file mode 100755 index 0000000000000000000000000000000000000000..8ffb2a912d571cb90ae0fce90df7319a9fe5049e --- /dev/null +++ b/src/secaudit.c @@ -0,0 +1,7777 @@ +/* + * Display and audit security attributes in an NTFS volume + * + * Copyright (c) 2007-2014 Jean-Pierre Andre + * + * Options : + * -a auditing security data + * -b backing up NTFS ACLs + * -e set extra backed-up parameters (in conjunction with -s) + * -h displaying hexadecimal security descriptors within a file + * -r recursing in a directory + * -s setting backed-up NTFS ACLs + * -u getting a user mapping proposal + * -v verbose (very verbose if set twice) + * also, if compile-time option is set + * -t run internal tests (with no access to storage) + * + * On Linux (being root, with volume not mounted) : + * secaudit -h [file] + * display the security descriptors found in file + * secaudit -a[rv] volume + * audit the volume + * secaudit [-v] volume file + * display the security parameters of file + * secaudit -r[v] volume directory + * display the security parameters of files in directory + * secaudit -b[v] volume [directory] + * backup the security parameters of files in directory + * secaudit -s[ve] volume [backupfile] + * set the security parameters as indicated in backup + * with -e set extra parameters (Windows attrib) + * secaudit volume perms file + * set the security parameters of file to perms (mode or acl) + * secaudit -r[v] volume perms directory + * set the security parameters of files in directory to perms + * special case, does not require being root : + * secaudit [-v] mounted-file + * display the security parameters of mounted file + * + * + * On Windows (the volume being part of file name) + * secaudit -h [file] + * display the security descriptors found in file + * secaudit [-v] file + * display the security parameters of file + * secaudit -r[v] directory + * display the security parameters of files in directory + * secaudit -b[v] directory + * backup the security parameters of files in directory + * secaudit -s[v] [backupfile] + * set the security parameters as indicated in backup + * with -e set extra parameters (Windows attrib) + * secaudit perms file + * set the security parameters of file to perms (mode or acl) + * secaudit -r[v] perms directory + * set the security parameters of files in directory to perms + */ + +/* History + * + * Nov 2007 + * - first version, by concatenating miscellaneous utilities + * + * Jan 2008, version 1.0.1 + * - fixed mode displaying + * - added a global severe errors count + * + * Feb 2008, version 1.0.2 + * - implemented conversions for big-endian machines + * + * Mar 2008, version 1.0.3 + * - avoided consistency checks on $Secure when there is no such file + * + * Mar 2008, version 1.0.4 + * - changed ordering of ACE's + * - changed representation for special flags + * - defaulted to stdin for option -h + * - added self tests (compile time option) + * - fixed errors specific to big-endian computers + * + * Apr 2008, version 1.1.0 + * - developped Posix ACLs to NTFS ACLs conversions + * - developped NTFS ACLs backup and restore + * + * Apr 2008, version 1.1.1 + * - fixed an error specific to big-endian computers + * - checked hash value and fixed error report in restore + * - improved display in showhex() and restore() + * + * Apr 2008, version 1.1.2 + * - improved and fixed Posix ACLs to NTFS ACLs conversions + * + * Apr 2008, version 1.1.3 + * - reenabled recursion for setting a new mode or ACL + * - processed Unicode file names and displayed them as UTF-8 + * - allocated dynamically memory for file names when recursing + * + * May 2008, version 1.1.4 + * - all Unicode/UTF-8 strings checked and processed + * + * Jul 2008, version 1.1.5 + * - made Windows owner consistent with Linux owner when changing mode + * - allowed owner change on Windows when it does not match Linux owner + * - skipped currently unused code + * + * Aug 2008, version 1.2.0 + * - processed \.NTFS-3G\UserMapping on Windows + * - made use of user mappings through the security API or direct access + * - fixed a bug in restore + * - fixed UTF-8 conversions + * + * Sep 2008, version 1.3.0 + * - split the code to have part of it shared with ntfs-3g library + * - fixed testing for end of SDS block + * - added samples checking in selftest for easier debugging + * + * Oct 2008, version 1.3.1 + * - fixed outputting long long data when testing on a Palm organizer + * + * Dec 2008, version 1.3.2 + * - fixed restoring ACLs + * - added optional logging of ACL hashes to facilitate restore checks + * - fixed collecting SACLs + * - fixed setting special control flags + * - fixed clearing existing SACLs (Linux only) and DACLs + * - changed the sequencing of items when quering a security descriptor + * - avoided recursing on junctions and symlinks on Windows + * + * Jan 2009, version 1.3.3 + * - save/restore Windows attributes (code from Faisal) + * + * Mar 2009, version 1.3.4 + * - enabled displaying attributes of a mounted file over Linux + * + * Apr 2009, version 1.3.5 + * - fixed initialisation of stand-alone user mapping + * - fixed POSIXACL redefinition when included in the ntfs-3g package + * - fixed displaying of options + * - fixed a dependency on the shared library version used + * + * May 2009, version 1.3.6 + * - added new samples for self testing + * + * Jun 2009, version 1.3.7 + * - fixed displaying owner and group of a mounted file over Linux + * + * Jul 2009, version 1.3.8 + * - fixed again displaying owner and group of a mounted file over Linux + * - cleaned some code to avoid warnings + * + * Nov 2009, version 1.3.9 + * - allowed security descriptors up to 64K + * + * Nov 2009, version 1.3.10 + * - applied patches for MacOSX from Erik Larsson + * + * Nov 2009, version 1.3.11 + * - replace <attr/xattr.h> by <sys/xattr.h> (provided by glibc) + * + * Dec 2009, version 1.3.12 + * - worked around "const" possibly redefined in config.h + * + * Dec 2009, version 1.3.13 + * - fixed the return code of dorestore() + * + * Dec 2009, version 1.3.14 + * - adapted to opensolaris + * + * Jan 2010, version 1.3.15 + * - more adaptations to opensolaris + * - removed the fix for return code of dorestore() + * + * Jan 2010, version 1.3.16 + * - repeated the fix for return code of dorestore() + * + * Mar 2010, version 1.3.17 + * - adapted to new default user mapping + * - fixed #ifdef'd code for selftest + * + * May 2010, version 1.3.18 + * - redefined early error logging + * + * Mar 2011, version 1.3.19 + * - fixed interface to ntfs_initialize_file_security() + * + * Apr 2011, version 1.3.20 + * - fixed false memory leak detection + * + * Jun 2011, version 1.3.21 + * - cleaned a few unneeded variables + * + * Nov 2011, version 1.3.22 + * - added a distinctive prefix to owner and group SID + * - fixed a false memory leak detection + * + * Jun 2012, version 1.3.23 + * - added support for SACL (nickgarvey) + * + * Jul 2012, version 1.3.24 + * - added self-tests for authenticated users + * - added display of ace-inherited flag + * - made runnable on OpenIndiana + * + * Aug 2012, version 1.4.0 + * - added an option for user mapping proposal + * + * Sep 2013, version 1.4.1 + * - silenced an aliasing warning by gcc >= 4.8 + * + * May 2014, version 1.4.2 + * - decoded GENERIC_ALL permissions + * - decoded more "well-known" and generic SIDs + * - showed Windows ownership in verbose situations + * - fixed apparent const violations + */ + +/* + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * General parameters which may have to be adapted to needs + */ + +#define AUDT_VERSION "1.4.2" + +#define GET_FILE_SECURITY "ntfs_get_file_security" +#define SET_FILE_SECURITY "ntfs_set_file_security" +#define GET_FILE_ATTRIBUTES "ntfs_get_file_attributes" +#define SET_FILE_ATTRIBUTES "ntfs_set_file_attributes" +#define READ_DIRECTORY "ntfs_read_directory" +#define READ_SDS "ntfs_read_sds" +#define READ_SII "ntfs_read_sii" +#define READ_SDH "ntfs_read_sdh" +#define GET_USER "ntfs_get_user" +#define GET_GROUP "ntfs_get_group" +#define GET_USID "ntfs_get_usid" +#define GET_GSID "ntfs_get_gsid" +#define INIT_FILE_SECURITY "ntfs_initialize_file_security" +#define LEAVE_FILE_SECURITY "ntfs_leave_file_security" + +/* + * External declarations + */ + +#include <stdio.h> +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> +#include <stdarg.h> + + /* + * integration into secaudit, check whether Win32, + * may have to be adapted to compiler or something else + */ + +#ifndef WIN32 +#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC) +#define WIN32 1 +#endif +#endif + + /* + * integration into secaudit/Win32 + */ +#ifdef WIN32 +#include <windows.h> +#define __LITTLE_ENDIAN 1234 +#define __BYTE_ORDER __LITTLE_ENDIAN +#else + /* + * integration into secaudit/STSC + */ +#ifdef STSC +#include <stat.h> +#undef __BYTE_ORDER +#define __BYTE_ORDER __BIG_ENDIAN +#else + /* + * integration into secaudit/Linux + */ + +#include <sys/stat.h> +#ifdef HAVE_ENDIAN_H +#include <endian.h> +#endif +#ifdef HAVE_MACHINE_ENDIAN_H +#include <machine/endian.h> +#endif +#include <unistd.h> +#include <dlfcn.h> +#endif /* STSC */ +#endif /* WIN32 */ +#include "secaudit.h" + +#ifndef WIN32 + +#ifndef STSC + +#if !defined(HAVE_CONFIG_H) && POSIXACLS && !defined(__SVR4) + /* require <sys/xattr.h> if not integrated into ntfs-3g package */ +#define HAVE_SETXATTR 1 +#endif + +#ifdef HAVE_CONFIG_H +#ifdef _FILE_OFFSET_BITS +#undef _FILE_OFFSET_BITS /* work around "_FILE_OFFSET_BITS" possibly already defined */ +#endif + /* <sys/xattr.h> according to config.h if integrated into ntfs-3g package */ +#include "config.h" +#ifdef const /* work around "const" possibly redefined in config.h */ +#undef const +#endif +#ifndef POSIXACLS +#define POSIXACLS 0 +#endif +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#else +#warning "The extended attribute package is not available" +#endif /* HAVE_SETXATTR */ + +#endif /* STSC */ + +#define NTFS_MNT_NONE 0 /* no flag for mounting the device */ +#define NTFS_MNT_RDONLY 1 /* flag for mounting the device read-only */ + +struct CALLBACK; + +typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname, + int length, int type, long long pos, u64 mft_ref, + unsigned int dt_type); + +#ifndef HAVE_SYSLOG_H +void ntfs_log_early_error(const char *format, ...) + __attribute__((format(printf, 1, 2))); +#endif + +#if USESTUBS | defined(STSC) + +int ntfs_get_file_security(void *scapi, + const char *path, DWORD selection, + char *buf, DWORD buflen, LPDWORD psize); +BOOL ntfs_set_file_security(void *scapi, + const char *path, DWORD selection, const char *attr); +int ntfs_get_file_attributes(void *scapi, + const char *path); +BOOL ntfs_set_file_attributes(void *scapi, + const char *path, DWORD attrib); +BOOL ntfs_read_directory(void *scapi, + const char *path, dircallback callback, void *context); +int ntfs_read_sds(void *scapi, + char *buf, DWORD buflen, DWORD offset); +void *ntfs_read_sii(void *scapi, void *entry); +void *ntfs_read_sdh(void *scapi, void *entry); + +int ntfs_get_usid(void *scapi, uid_t uid, char *buf); +int ntfs_get_gsid(void *scapi, gid_t gid, char *buf); +int ntfs_get_user(void *scapi, const char *usid); +int ntfs_get_group(void *scapi, const char *gsid); + +void *ntfs_initialize_file_security(const char *device, unsigned long flags); +BOOL ntfs_leave_file_security(void *scapi); + +#else + +typedef int (*type_get_file_security)(void *scapi, + const char *path, DWORD selection, + char *buf, DWORD buflen, LPDWORD psize); +typedef BOOL (*type_set_file_security)(void *scapi, + const char *path, DWORD selection, const char *attr); +typedef int (*type_get_file_attributes)(void *scapi, + const char *path); +typedef BOOL (*type_set_file_attributes)(void *scapi, + const char *path, DWORD attrib); +typedef BOOL (*type_read_directory)(void *scapi, + const char *path, dircallback callback, void *context); +typedef int (*type_read_sds)(void *scapi, + char *buf, DWORD buflen, DWORD offset); +typedef void *(*type_read_sii)(void *scapi, void *entry); +typedef void *(*type_read_sdh)(void *scapi, void *entry); + +typedef int (*type_get_usid)(void *scapi, uid_t uid, char *buf); +typedef int (*type_get_gsid)(void *scapi, gid_t gid, char *buf); +typedef int (*type_get_user)(void *scapi, const char *usid); +typedef int (*type_get_group)(void *scapi, const char *gsid); + +typedef void *(*type_initialize_file_security)(const char *device, + unsigned long flags); +typedef BOOL (*type_leave_file_security)(void *scapi); + +type_get_file_security ntfs_get_file_security; +type_set_file_security ntfs_set_file_security; +type_get_file_attributes ntfs_get_file_attributes; +type_set_file_attributes ntfs_set_file_attributes; +type_read_directory ntfs_read_directory; +type_read_sds ntfs_read_sds; +type_read_sii ntfs_read_sii; +type_read_sdh ntfs_read_sdh; + +type_get_usid ntfs_get_usid; +type_get_gsid ntfs_get_gsid; +type_get_user ntfs_get_user; +type_get_group ntfs_get_group; + +type_initialize_file_security ntfs_initialize_file_security; +type_leave_file_security ntfs_leave_file_security; + + +#endif /* USESTUBS | defined(STSC) */ +#endif /* WIN32 */ + +#define ACCOUNTSIZE 256 /* maximum size of an account name */ + +/* + * Prototypes for local functions + */ + +BOOL open_security_api(void); +BOOL close_security_api(void); +#ifndef WIN32 +BOOL open_volume(const char*, unsigned long flags); +BOOL close_volume(const char*); +#endif +unsigned int get2l(const char*, int); +unsigned long get4l(const char*, int); +u64 get6l(const char*, int); +u64 get6h(const char*, int); +u64 get8l(const char*, int); +void set2l(char*, unsigned int); +void set4l(char*, unsigned long); +void hexdump(const char*, int, int); +u32 hash(const le32*, int); +unsigned int utf8size(const char*, int); +unsigned int makeutf8(char*, const char*, int); +unsigned int utf16size(const char*); +unsigned int makeutf16(char*, const char*); +unsigned int utf16len(const char*); +void printname(FILE*, const char*); +void printerror(FILE*); +BOOL guess_dir(const char*); +void showsid(const char*, int, const char*, int); +void showusid(const char*, int); +void showgsid(const char*, int); +void showownership(const char*); +void showheader(const char*, int); +void showace(const char*, int, int, int); +void showacl(const char*, int, int, int); +void showdacl(const char*, int, int); +void showsacl(const char*, int, int); +void showall(const char*, int); +void showposix(const struct POSIX_SECURITY*); +int linux_permissions(const char*, BOOL); +uid_t linux_owner(const char*); +gid_t linux_group(const char*); +int basicread(void*, char*, size_t, off_t); +int dummyread(void*, char*, size_t, off_t); +int local_build_mapping(struct MAPPING *[], const char*); +void newblock(s32); +void freeblocks(void); +u32 getmsbhex(const char*); +u32 getlsbhex(const char*); +BOOL ishexdump(const char*, int, int); +void showhex(FILE*); +void showfull(const char*, BOOL); +BOOL applyattr(const char*, const char*, BOOL, int, s32); +BOOL restore(FILE*); +BOOL dorestore(const char*, FILE*); +u32 merge_rights(const struct POSIX_SECURITY*, BOOL); +void tryposix(struct POSIX_SECURITY*); +void check_samples(void); +void basictest(int, BOOL, const SID*, const SID*); +void posixtest(int, BOOL, const SID*, const SID*); +void selftests(void); +unsigned int getfull(char*, const char*); +BOOL updatefull(const char *name, DWORD flags, char *attr); +BOOL setfull(const char*, int, BOOL); +BOOL singleshow(const char*); +BOOL proposal(const char*, const char*); +BOOL showmounted(const char*); +BOOL processmounted(const char*); +BOOL recurseshow(const char*); +BOOL singleset(const char*, int); +BOOL recurseset(const char*, int); +#ifdef WIN32 +BOOL backup(const char*); +BOOL listfiles(const char*); +#if POSIXACLS +BOOL iterate(RECURSE, const char*, const struct POSIX_SECURITY*); +#else +BOOL iterate(RECURSE, const char*, mode_t); +#endif +#else +BOOL backup(const char*, const char*); +BOOL listfiles(const char*, const char*); +BOOL mapproposal(const char*, const char*); +#endif +#if POSIXACLS +BOOL setfull_posix(const char *, const struct POSIX_SECURITY*, BOOL); +struct POSIX_SECURITY *linux_permissions_posix(const char*, BOOL); +BOOL recurseset_posix(const char*, const struct POSIX_SECURITY*); +BOOL singleset_posix(const char*, const struct POSIX_SECURITY*); +struct POSIX_SECURITY *encode_posix_acl(const char*); +#endif +static void *stdmalloc(size_t); +static void stdfree(void*); + +BOOL valid_sds(const char*, unsigned int, unsigned int, + unsigned int, u32, BOOL); +BOOL valid_sii(const char*, u32); +BOOL valid_sdh(const char*, u32, u32); +int consist_sds(const char*, unsigned int, unsigned int, BOOL); +int consist_sii(const char*); +int consist_sdh(const char*); +int audit_sds(BOOL); +int audit_sii(void); +int audit_sdh(void); +void audit_summary(void); +BOOL audit(const char*); +int getoptions(int, char*[]); + +#ifndef WIN32 + +/* + * Structures for collecting directory contents (Linux only) + */ + +struct LINK { + struct LINK *next; + char name[1]; +} ; + +struct CALLBACK { + struct LINK *head; + const char *dir; +} ; + +int callback(struct CALLBACK *context, char *ntfsname, + int length, int type, long long pos, u64 mft_ref, + unsigned int dt_type); +#endif + +/* + * Global constants + */ + +#define BANNER "secaudit " AUDT_VERSION " : NTFS security data auditing" + +#if SELFTESTS & !USESTUBS + +/* + * Dummy mapping file (self tests only) + */ + +#define DUMMYAUTH "S-1-5-21-3141592653-589793238-462843383-" +char dummymapping[] = "500::" DUMMYAUTH "1000\n" + "501::" DUMMYAUTH "1001\n" + "502::" DUMMYAUTH "1002\n" + "503::" DUMMYAUTH "1003\n" + "516::" DUMMYAUTH "1016\n" + ":500:" DUMMYAUTH "513\r\n" + ":511:S-1-5-21-1607551490-981732888-1819828000-513\n" + ":516:" DUMMYAUTH "1012\r\n" + "::" DUMMYAUTH "10000\n"; + +/* + * SID for world (S-1-1-0) + */ + +static const char worldsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 1, /* base */ + 0, 0, 0, 0 /* 1st level */ +} ; +static const SID *worldsid = (const SID*)worldsidbytes; + +/* + * SID for authenticated user (S-1-5-11) + */ + +static const char authsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 11, 0, 0, 0 /* 1st level */ +}; + +static const SID *authsid = (const SID*)authsidbytes; + +/* + * SID for administrator (S-1-5-32-544) + */ + +static const char adminsidbytes[] = { + 1, /* revision */ + 2, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 32, 0, 0, 0, /* 1st level */ + 32, 2, 0, 0 /* 2nd level */ +}; + +static const SID *adminsid = (const SID*)adminsidbytes; + +/* + * SID for local users (S-1-5-32-545) + */ + +static const char localsidbytes[] = { + 1, /* revision */ + 2, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 32, 0, 0, 0, /* 1st level */ + 33, 2, 0, 0 /* 2nd level */ +}; + +static const SID *localsid = (const SID*)localsidbytes; + +/* + * SID for system (S-1-5-18) + */ + +static const char systemsidbytes[] = { + 1, /* revision */ + 1, /* auth count */ + 0, 0, 0, 0, 0, 5, /* base */ + 18, 0, 0, 0 /* 1st level */ + }; + +static const SID *systemsid = (const SID*)systemsidbytes; + +#endif + +/* + * Global variables + */ + +BOOL opt_a; /* audit security data */ +BOOL opt_b; /* backup NTFS ACLs */ +BOOL opt_e; /* restore extra (currently windows attribs) */ +BOOL opt_h; /* display an hexadecimal descriptor in a file */ +BOOL opt_r; /* recursively apply to subdirectories */ +BOOL opt_s; /* restore NTFS ACLs */ +BOOL opt_u; /* user mapping proposal */ +#if SELFTESTS & !USESTUBS +BOOL opt_t; /* run self-tests */ +#endif +int opt_v; /* verbose or very verbose*/ +struct SECURITY_DATA *securdata[(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ)]; +#if FORCEMASK +BOOL opt_m; /* force a mask - dangerous */ +u32 forcemsk /* mask to force */ +#endif +unsigned int errors; /* number of severe errors */ +unsigned int warnings; /* number of non-severe errors */ + +struct CHKALLOC *firstalloc; +struct SECURITY_CONTEXT context; +MAPTYPE mappingtype; + +#ifdef STSC +#define static +#endif + +#ifndef WIN32 + +void *ntfs_handle; +void *ntfs_context = (void*)NULL; + +/* + * Open and close the security API (platform dependent) + */ + +BOOL open_security_api(void) +{ +#if USESTUBS | defined(STSC) + return (TRUE); +#else + char *error; + BOOL err; + const char *libfile; + + err = TRUE; + libfile = getenv(ENVNTFS3G); + if (!libfile) + libfile = (sizeof(char*) == 8 ? LIBFILE64 : LIBFILE); +#ifdef __SVR4 + /* do not override library functions by caller ones */ + ntfs_handle = dlopen(libfile,RTLD_LAZY | RTLD_GROUP); +#else + ntfs_handle = dlopen(libfile,RTLD_LAZY); +#endif + if (ntfs_handle) { + ntfs_initialize_file_security = (type_initialize_file_security) + dlsym(ntfs_handle,INIT_FILE_SECURITY); + error = dlerror(); + if (error) + fprintf(stderr," %s\n",error); + else { + ntfs_leave_file_security = (type_leave_file_security) + dlsym(ntfs_handle,LEAVE_FILE_SECURITY); + ntfs_get_file_security = (type_get_file_security) + dlsym(ntfs_handle,GET_FILE_SECURITY); + ntfs_set_file_security = (type_set_file_security) + dlsym(ntfs_handle,SET_FILE_SECURITY); + ntfs_get_file_attributes = (type_get_file_attributes) + dlsym(ntfs_handle,GET_FILE_ATTRIBUTES); + ntfs_set_file_attributes = (type_set_file_attributes) + dlsym(ntfs_handle,SET_FILE_ATTRIBUTES); + ntfs_read_directory = (type_read_directory) + dlsym(ntfs_handle,READ_DIRECTORY); + ntfs_read_sds = (type_read_sds) + dlsym(ntfs_handle,READ_SDS); + ntfs_read_sii = (type_read_sii) + dlsym(ntfs_handle,READ_SII); + ntfs_read_sdh = (type_read_sdh) + dlsym(ntfs_handle,READ_SDH); + ntfs_get_user = (type_get_user) + dlsym(ntfs_handle,GET_USER); + ntfs_get_group = (type_get_group) + dlsym(ntfs_handle,GET_GROUP); + ntfs_get_usid = (type_get_usid) + dlsym(ntfs_handle,GET_USID); + ntfs_get_gsid = (type_get_gsid) + dlsym(ntfs_handle,GET_GSID); + err = !ntfs_initialize_file_security + || !ntfs_leave_file_security + || !ntfs_get_file_security + || !ntfs_set_file_security + || !ntfs_get_file_attributes + || !ntfs_set_file_attributes + || !ntfs_read_directory + || !ntfs_read_sds + || !ntfs_read_sii + || !ntfs_read_sdh + || !ntfs_get_user + || !ntfs_get_group + || !ntfs_get_usid + || !ntfs_get_gsid; + + if (error) + fprintf(stderr,"ntfs-3g API not available\n"); + } + } else { + fprintf(stderr,"Could not open ntfs-3g library\n"); + fprintf(stderr,"\nPlease set environment variable \"" ENVNTFS3G "\"\n"); + fprintf(stderr,"to appropriate path and retry\n"); + } + return (!err); +#endif /* USESTUBS | defined(STSC) */ +} + +BOOL close_security_api(void) +{ +#if USESTUBS | defined(STSC) + return (0); +#else + return (!dlclose(ntfs_handle)); +#endif /* USESTUBS | defined(STSC) */ +} + +/* + * Open and close a volume (platform dependent) + * Assumes a single volume is opened + */ + +BOOL open_volume(const char *volume, unsigned long flags) +{ + BOOL ok; + + ok = FALSE; + if (!ntfs_context) { + ntfs_context = ntfs_initialize_file_security(volume,flags); + if (ntfs_context) { + if (*(u32*)ntfs_context != MAGIC_API) { + fprintf(stderr,"Versions of ntfs-3g and secaudit" + " are not compatible\n"); + } else { + fprintf(stderr,"\"%s\" opened\n",volume); + mappingtype = MAPEXTERN; + ok = TRUE; + } + } else { + fprintf(stderr,"Could not open \"%s\"\n",volume); + fprintf(stderr,"Make sure \"%s\" is not mounted\n",volume); + } + } else + fprintf(stderr,"A volume is already open\n"); + return (ok); +} + +BOOL close_volume(const char *volume) +{ + BOOL r; + + r = ntfs_leave_file_security(ntfs_context); + if (r) + fprintf(stderr,"\"%s\" closed\n",volume); + else + fprintf(stderr,"Could not close \"%s\"\n",volume); + ntfs_context = (void*)NULL; + return (r); +} + +#endif /* WIN32 */ + +/* + * Extract small or big endian data from an array of bytes + */ + +unsigned int get2l(const char *attr, int p) +{ + int i; + unsigned int v; + + v = 0; + for (i=0; i<2; i++) + v += (attr[p+i] & 255) << (8*i); + return (v); +} + +unsigned long get4l(const char *attr, int p) +{ + int i; + unsigned long v; + + v = 0; + for (i=0; i<4; i++) + v += ((long)(attr[p+i] & 255)) << (8*i); + return (v); +} + +u64 get6l(const char *attr, int p) +{ + int i; + u64 v; + + v = 0; + for (i=0; i<6; i++) + v += ((long long)(attr[p+i] & 255)) << (8*i); + return (v); +} + +u64 get6h(const char *attr, int p) +{ + int i; + u64 v; + + v = 0; + for (i=0; i<6; i++) + v = (v << 8) + (attr[p+i] & 255); + return (v); +} + +u64 get8l(const char *attr, int p) +{ + int i; + u64 v; + + v = 0; + for (i=0; i<8; i++) + v += ((long long)(attr[p+i] & 255)) << (8*i); + return (v); +} + +/* + * Set small or big endian data into an array of bytes + */ + +void set2l(char *p, unsigned int v) +{ + int i; + + for (i=0; i<2; i++) + p[i] = ((v >> 8*i) & 255); +} + +void set4l(char *p, unsigned long v) +{ + int i; + + for (i=0; i<4; i++) + p[i] = ((v >> 8*i) & 255); +} + + +/* + * hexadecimal dump of an array of bytes + */ + +void hexdump(const char *attr, int size, int level) +{ + int i,j; + + for (i=0; i<size; i+=16) { + if (level) + printf("%*c",level,' '); + printf("%06x ",i); + for (j=i; (j<(i+16)) && (j<size); j++) + printf((j & 3 ? "%02x" : " %02x"),attr[j] & 255); + printf("\n"); + } +} + +u32 hash(const le32 *buf, int size /* bytes */) +{ + u32 h; + int i; + + h = 0; + for (i=0; 4*i<size; i++) + h = le32_to_cpu(buf[i]) + (h << 3) + ((h >> 29) & 7); + return (h); +} + +/* + * Evaluate the size of UTS-8 conversion of a UTF-16LE text + * trailing '\0' not accounted for + * Returns 0 for invalid input + */ + +unsigned int utf8size(const char *utf16, int length) +{ + int i; + unsigned int size; + enum { BASE, SURR, ERR } state; + + size = 0; + state = BASE; + for (i=0; i<2*length; i+=2) { + switch (state) { + case BASE : + if (utf16[i+1] & 0xf8) { + if ((utf16[i+1] & 0xf8) == 0xd8) + state = (utf16[i+1] & 4 ? ERR : SURR); + else +#if NOREVBOM + if (((utf16[i+1] & 0xff) == 0xff) + && ((utf16[i] & 0xfe) == 0xfe)) + state = ERR; + else + size += 3; +#else + size += 3; +#endif + } else + if ((utf16[i] & 0x80) || utf16[i+1]) + size += 2; + else + size++; + break; + case SURR : + if ((utf16[i+1] & 0xfc) == 0xdc) { + state = BASE; + size += 4; + } else + state = ERR; + break; + case ERR : + break; + } + } + if (state != BASE) + size = 0; + return (size); +} + +/* + * Convert a UTF-16LE text to UTF-8 + * Note : wcstombs() not used because on Linux it fails for characters + * not present in current locale + * Returns size or zero for invalid input + */ + +unsigned int makeutf8(char *utf8, const char *utf16, int length) +{ + int i; + unsigned int size; + unsigned int rem; + enum { BASE, SURR, ERR } state; + + size = 0; + rem = 0; + state = BASE; + for (i=0; i<2*length; i+=2) { + switch (state) { + case BASE : + if (utf16[i+1] & 0xf8) { + if ((utf16[i+1] & 0xf8) == 0xd8) { + if (utf16[i+1] & 4) + state = ERR; + else { + utf8[size++] = 0xf0 + (utf16[i+1] & 7) + + ((utf16[i] & 0xc0) == 0xc0); + utf8[size++] = 0x80 + (((utf16[i] + 64) >> 2) & 63); + rem = utf16[i] & 3; + state = SURR; + } + } else { +#if NOREVBOM + if (((utf16[i+1] & 0xff) == 0xff) + && ((utf16[i] & 0xfe) == 0xfe)) + state = ERR; + else { + utf8[size++] = 0xe0 + ((utf16[i+1] >> 4) & 15); + utf8[size++] = 0x80 + + ((utf16[i+1] & 15) << 2) + + ((utf16[i] >> 6) & 3); + utf8[size++] = 0x80 + (utf16[i] & 63); + } +#else + utf8[size++] = 0xe0 + ((utf16[i+1] >> 4) & 15); + utf8[size++] = 0x80 + + ((utf16[i+1] & 15) << 2) + + ((utf16[i] >> 6) & 3); + utf8[size++] = 0x80 + (utf16[i] & 63); +#endif + } + } else + if ((utf16[i] & 0x80) || utf16[i+1]) { + utf8[size++] = 0xc0 + + ((utf16[i+1] & 15) << 2) + + ((utf16[i] >> 6) & 3); + utf8[size++] = 0x80 + (utf16[i] & 63); + } else + utf8[size++] = utf16[i]; + break; + case SURR : + if ((utf16[i+1] & 0xfc) == 0xdc) { + utf8[size++] = 0x80 + (rem << 4) + + ((utf16[i+1] & 3) << 2) + + ((utf16[i] >> 6) & 3); + utf8[size++] = 0x80 + (utf16[i] & 63); + state = BASE; + } else + state = ERR; + break; + case ERR : + break; + } + } + utf8[size] = 0; + if (state != BASE) + state = ERR; + return (state == ERR ? 0 : size); +} + +#ifdef WIN32 + +/* + * Evaluate the size of UTF-16LE conversion of a UTF-8 text + * (basic conversions only) + * trailing '\0' not accounted for + */ + +unsigned int utf16size(const char *utf8) +{ + unsigned int size; + const char *p; + int c; + unsigned int code; + enum { BASE, TWO, THREE, THREE2, THREE3, FOUR, ERR } state; + + p = utf8; + size = 0; + state = BASE; + while (*p) { + c = *p++ & 255; + switch (state) { + case BASE : + if (!(c & 0x80)) + size++; + else + if (c < 0xc2) + state = ERR; + else + if (c < 0xe0) + state = TWO; + else + if (c < 0xf0) { + if (c == 0xe0) + state = THREE2; + else + if (c == 0xed) + state = THREE3; + else + state = THREE; + } else + if (c < 0xf8) { + state = FOUR; + code = c & 7; + } else + state = ERR; + break; + case TWO : + if ((c & 0xc0) != 0x80) + state = ERR; + else { + size++; + state = BASE; + } + break; + case THREE : + if ((c & 0xc0) != 0x80) + state = ERR; + else + state = TWO; + break; + case THREE2 : + if ((c & 0xe0) != 0xa0) + state = ERR; + else + state = TWO; + break; + case THREE3 : + if ((c & 0xe0) != 0x80) + state = ERR; + else + state = TWO; + break; + case FOUR : + if ((((code << 6) + (c & 63)) > 0x10f) + || (((code << 6) + (c & 63)) < 0x10)) + state = ERR; + else { + size++; + state = THREE; + } + break; + case ERR : + break; + } + } + if (state != BASE) size = 0; + return (size); +} + +/* + * Convert a UTF8 text to UTF-16LE + * (basic conversions only) + * Note : mbstowcs() not used because on Linux it fails for characters + * not present in current locale + */ + +unsigned int makeutf16(char *target, const char *utf8) +{ + unsigned int size; + unsigned int code; + const char *p; + int c; + enum { BASE, TWO, THREE, THREE2, THREE3, FOUR, FOUR2, FOUR3, ERR } state; + + p = utf8; + size = 0; + c = 0; + state = BASE; + while (*p) { + c = *p++ & 255; + switch (state) { + case BASE : + if (!(c & 0x80)) { + target[2*size] = c; + target[2*size + 1] = 0; + size++; + } else { + if (c < 0xc2) + state = ERR; + else + if (c < 0xe0) { + code = c & 31; + state = TWO; + } else + if (c < 0xf0) { + code = c & 15; + if (c == 0xe0) + state = THREE2; + else + if (c == 0xed) + state = THREE3; + else + state = THREE; + } else + if (c < 0xf8) { + code = c & 7; + state = FOUR; + } else + state = ERR; + } + break; + case TWO : +#if NOREVBOM + if (((c & 0xc0) != 0x80) + || ((code == 0x3ff) && (c >= 0xbe))) +#else + if ((c & 0xc0) != 0x80) +#endif + state = ERR; + else { + target[2*size] = ((code & 3) << 6) + (c & 63); + target[2*size + 1] = ((code >> 2) & 255); + size++; + state = BASE; + } + break; + case THREE : + if ((c & 0xc0) != 0x80) + state = ERR; + else { + code = ((code & 15) << 6) + (c & 63); + state = TWO; + } + break; + case THREE2 : + if ((c & 0xe0) != 0xa0) + state = ERR; + else { + code = ((code & 15) << 6) + (c & 63); + state = TWO; + } + break; + case THREE3 : + if ((c & 0xe0) != 0x80) + state = ERR; + else { + code = ((code & 15) << 6) + (c & 63); + state = TWO; + } + break; + case FOUR : + if ((c & 0xc0) != 0x80) + state = ERR; + else { + code = (code << 6) + (c & 63); + state = FOUR2; + } + break; + case FOUR2 : + if ((c & 0xc0) != 0x80) + state = ERR; + else { + code = (code << 6) + (c & 63); + state = FOUR3; + } + break; + case FOUR3 : + if ((code > 0x43ff) + || (code < 0x400) + || ((c & 0xc0) != 0x80)) + state = ERR; + else { + target[2*size] = ((code - 0x400) >> 4) & 255; + target[2*size+1] = 0xd8 + (((code - 0x400) >> 12) & 3); + target[2*size+2] = ((code & 3) << 6) + (c & 63); + target[2*size+3] = 0xdc + ((code >> 2) & 3); + size += 2; + state = BASE; + } + break; + case ERR : + break; + } + } + if (state != BASE) + size = 0; + target[2*size] = 0; + target[2*size + 1] = 0; + return (size); +} + +unsigned int utf16len(const char *str) +{ + unsigned int len; + + len = 0; + while (str[2*len] || str[2*len+1]) len++; + return (len); +} + +#endif + +/* + * Print a file name + * on Windows it prints UTF-16LE names as UTF-8 + */ + +void printname(FILE *file, const char *name) +{ +#ifdef WIN32 + char utf8name[MAXFILENAME]; + + makeutf8(utf8name,name,utf16len(name)); + fprintf(file,"%s",utf8name); +#else + fprintf(file,"%s",name); +#endif +} + +/* + * Print the last error code + */ + +void printerror(FILE *file) +{ +#ifdef WIN32 + int err; + const char *txt; + + err = GetLastError(); + switch (err) { + case 5 : + txt = "Access to security descriptor was denied"; + break; + case 1307 : + txt = "This SID may not be assigned as the owner of this object"; + break; + case 1308 : + txt = "This SID may not be assigned as the group of this object"; + break; + case 1314 : + txt = "You do not have the privilege to change this SID"; + break; + default : + txt = (const char*)NULL; + break; + } + if (txt) + fprintf(file,"Error %d : %s\n",err,txt); + else + fprintf(file,"Error %d\n",err); +#else +#ifdef STSC + if (errno) fprintf(file,"Error code %d\n",errno); +#else + if (errno) fprintf(file,"Error code %d : %s\n",errno,strerror(errno)); +#endif +#endif +} + +#ifndef HAVE_SYSLOG_H + +/* + * Redefine early error messages in stand-alone situations + */ + +void ntfs_log_early_error(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stderr,format,args); + va_end(args); +} + +#endif + +/* + * Guess whether a security attribute is intended for a directory + * based on the presence of inheritable ACE + * (not 100% reliable) + */ + +BOOL guess_dir(const char *attr) +{ + int off; + int isdir; + int cnt; + int i; + int x; + + isdir = 0; + off = get4l(attr,16); + if (off) { + cnt = get2l(attr,off+4); + x = 8; + for (i=0; i<cnt; i++) { + if (attr[off + x + 1] & 3) + isdir = 1; + x += get2l(attr,off + x + 2); + } + } + return (isdir); +} + +/* + * Display a SID + * See http://msdn2.microsoft.com/en-us/library/aa379649.aspx + */ + +void showsid(const char *attr, int off, const char *prefix, int level) +{ + int cnt; + int i; + BOOL known; + u64 auth; + unsigned long first; + unsigned long second; + unsigned long last; + char marker; + + if (opt_b) + marker = '#'; + else + marker = ' '; + cnt = attr[off+1] & 255; + auth = get6h(attr,off+2); + first = get4l(attr,off+8); + known = FALSE; + if ((attr[off] == 1) /* revision */ + && (auth < 100)) + switch (cnt) { + case 0 : /* no level (error) */ + break; + case 1 : /* single level */ + switch (auth) { + case 0 : + if (first == 0) { + known = TRUE; + printf("%*cNull SID\n",-level,marker); + } + break; + case 1 : + if (first == 0) { + known = TRUE; + printf("%*cWorld SID\n",-level,marker); + } + break; + case 3 : + switch (first) { + case 0 : + known = TRUE; + printf("%*cCreator owner SID\n",-level,marker); + break; + case 1 : + known = TRUE; + printf("%*cCreator group SID\n",-level,marker); + break; + } + break; + case 5 : + switch (first) { + case 1 : + known = TRUE; + printf("%*cDialup SID\n",-level,marker); + break; + case 2 : + known = TRUE; + printf("%*cNetwork SID\n",-level,marker); + break; + case 3 : + known = TRUE; + printf("%*cBatch SID\n",-level,marker); + break; + case 4 : + known = TRUE; + printf("%*cInteractive SID\n",-level,marker); + break; + case 6 : + known = TRUE; + printf("%*cService SID\n",-level,marker); + break; + case 7 : + known = TRUE; + printf("%*cAnonymous logon SID\n",-level,marker); + break; + case 11 : + known = TRUE; + printf("%*cAuthenticated user SID\n",-level,marker); + break; + case 13 : + known = TRUE; + printf("%*cLocal service SID\n",-level,marker); + break; + case 14 : + known = TRUE; + printf("%*cNetwork service SID\n",-level,marker); + break; + case 18 : + known = TRUE; + printf("%*cNT System SID\n",-level,marker); + break; + } + break; + } + break; + case 2 : /* double level */ + second = get4l(attr,off+12); + switch (auth) { + case 5 : + if (first == 32) { + known = TRUE; + switch (second) { + case 544 : + printf("%*cLocal admins SID\n",-level,marker); + break; + case 545 : + printf("%*cLocal users SID\n",-level,marker); + break; + case 546 : + printf("%*cLocal guests SID\n",-level,marker); + break; + default : + printf("%*cSome domain SID\n",-level,marker); + break; + } + } + break; + } + default : /* three levels or more */ + second = get4l(attr,off+12); + last = get4l(attr,off+4+4*cnt); + switch (auth) { + case 5 : + if (first == 21) { + known = TRUE; + switch (last) { + case 500 : + printf("%*cSystem admin SID\n",-level,marker); + break; + case 501 : + printf("%*cGuest SID\n",-level,marker); + break; + case 512 : + printf("%*cLocal admins SID\n",-level,marker); + break; + case 513 : + printf("%*cLocal users SID\n",-level,marker); + break; + case 514 : + printf("%*cLocal guests SID\n",-level,marker); + break; + default : + printf("%*cLocal user-%lu SID\n",-level,marker,last); + break; + } + } + break; + } + } + if (!known) + printf("%*cUnknown SID\n",-level,marker); + printf("%*c%shex S-%d-",-level,marker,prefix,attr[off] & 255); + printf("%llx",auth); + for (i=0; i<cnt; i++) + printf("-%lx",get4l(attr,off+8+4*i)); + printf("\n"); + printf("%*c%sdec S-%d-",-level,marker,prefix,attr[off] & 255); + printf("%llu",auth); + for (i=0; i<cnt; i++) + printf("-%lu",get4l(attr,off+8+4*i)); + printf("\n"); +} + +void showusid(const char *attr, int level) +{ + int off; + char marker; + + if (opt_b) + marker = '#'; + else + marker = ' '; + if (level) + printf("%*c",-level,marker); + printf("Owner SID\n"); + off = get4l(attr,4); + showsid(attr,off,"O:",level+4); +} + +void showgsid(const char *attr, int level) +{ + int off; + char marker; + + if (opt_b) + marker = '#'; + else + marker = ' '; + if (level) + printf("%*c",-level,marker); + printf("Group SID\n"); + off = get4l(attr,8); + showsid(attr,off,"G:",level+4); +} + +void showownership(const char *attr) +{ +#ifdef WIN32 + char account[ACCOUNTSIZE]; + BIGSID sidcopy; + SID_NAME_USE use; + unsigned long accountsz; + unsigned long domainsz; +#endif + enum { SHOWOWN, SHOWGRP, SHOWINT } shown; + const char *sid; + const char *prefix; + u64 auth; + int cnt; + int off; + int i; + + for (shown=SHOWOWN; shown<=SHOWINT; shown++) { + switch (shown) { + case SHOWOWN : + off = get4l(attr,4); + sid = &attr[off]; + prefix = "Windows owner"; + break; + case SHOWGRP : + off = get4l(attr,8); + sid = &attr[off]; + prefix = "Windows group"; + break; +#if OWNERFROMACL + case SHOWINT : + off = get4l(attr,4); + prefix = "Interpreted owner"; + sid = (const char*)ntfs_acl_owner((const char*)attr); + if (ntfs_same_sid((const SID*)sid, + (const SID*)&attr[off])) + sid = (const char*)NULL; + break; +#endif + default : + sid = (const char*)NULL; + prefix = (const char*)NULL; + break; + } + if (sid) { + cnt = sid[1] & 255; + auth = get6h(sid,2); + if (opt_b) + printf("# %s S-%d-",prefix,sid[0] & 255); + else + printf("%s S-%d-",prefix,sid[0] & 255); + printf("%llu",auth); + for (i=0; i<cnt; i++) + printf("-%lu",get4l(sid,8+4*i)); +#ifdef WIN32 + memcpy(sidcopy,sid,ntfs_sid_size((const SID*)sid)); + accountsz = ACCOUNTSIZE; + domainsz = ACCOUNTSIZE; + if (LookupAccountSidA((const char*)NULL, sidcopy, + account, &accountsz, + (char*)NULL, &domainsz, &use)) + printf(" (%s)", account); +#endif + printf("\n"); + } + } +} + +void showheader(const char *attr, int level) +{ + int flags; + char marker; + + if (opt_b) + marker = '#'; + else + marker = ' '; + if (level) + printf("%*c",-level,marker); + printf("Global header\n"); + printf("%*crevision %d\n",-level-4,marker,attr[0]); + flags = get2l(attr,2); + printf("%*cflags 0x%x\n",-level-4,marker,flags); + if (flags & 1) + printf("%*c owner is defaulted\n",-level-4,marker); + if (flags & 2) + printf("%*c group is defaulted\n",-level-4,marker); + if (flags & 4) + printf("%*c DACL present\n",-level-4,marker); + if (flags & 8) + printf("%*c DACL is defaulted\n",-level-4,marker); + if (flags & 0x10) + printf("%*c SACL present\n",-level-4,marker); + if (flags & 0x20) + printf("%*c SACL is defaulted\n",-level-4,marker); + if (flags & 0x100) + printf("%*c DACL inheritance is requested\n",-level-4,marker); + if (flags & 0x200) + printf("%*c SACL inheritance is requested\n",-level-4,marker); + if (flags & 0x400) + printf("%*c DACL was inherited automatically\n",-level-4,marker); + if (flags & 0x800) + printf("%*c SACL was inherited automatically\n",-level-4,marker); + if (flags & 0x1000) + printf("%*c DACL cannot be modified by inheritable ACEs\n",-level-4,marker); + if (flags & 0x2000) + printf("%*c SACL cannot be modified by inheritable ACEs\n",-level-4,marker); + if (flags & 0x8000) + printf("%*c self relative descriptor\n",-level-4,marker); + if (flags & 0x43eb) + printf("%*c unknown flags 0x%x present\n",-level-4,marker, + flags & 0x43eb); + printf("%*cOff USID 0x%x\n",-level-4,marker,(int)get4l(attr,4)); + printf("%*cOff GSID 0x%x\n",-level-4,marker,(int)get4l(attr,8)); + printf("%*cOff SACL 0x%x\n",-level-4,marker,(int)get4l(attr,12)); + printf("%*cOff DACL 0x%x\n",-level-4,marker,(int)get4l(attr,16)); +} + +void showace(const char *attr, int off, int isdir, int level) +{ + int flags; + u32 rights; + char marker; + + if (opt_b) + marker = '#'; + else + marker = ' '; + printf("%*ctype %d\n",-level,marker,attr[off]); + switch (attr[off]) { + case 0 : + printf("%*cAccess allowed\n",-level-4,marker); + break; + case 1 : + printf("%*cAccess denied\n",-level-4,marker); + break; + case 2 : + printf("%*cSystem audit\n",-level-4,marker); + break; + default : + printf("%*cunknown\n",-level-4,marker); + break; + } + flags = attr[off+1] & 255; + printf("%*cflags 0x%x\n",-level,marker,flags); + if (flags & 1) + printf("%*cObject inherits ACE\n",-level-4,marker); + if (flags & 2) + printf("%*cContainer inherits ACE\n",-level-4,marker); + if (flags & 4) + printf("%*cDon\'t propagate inherits ACE\n",-level-4,marker); + if (flags & 8) + printf("%*cInherit only ACE\n",-level-4,marker); + if (flags & 0x10) + printf("%*cACE was inherited\n",-level-4,marker); + if (flags & 0x40) + printf("%*cAudit on success\n",-level-4,marker); + if (flags & 0x80) + printf("%*cAudit on failure\n",-level-4,marker); + + printf("%*cSize 0x%x\n",-level,marker,get2l(attr,off+2)); + + rights = get4l(attr,off+4); + printf("%*cAcc rgts 0x%lx\n",-level,marker,(long)rights); + printf("%*cObj specific acc rgts 0x%lx\n",-level-4,marker,(long)rights & 65535); + if (isdir) /* a directory */ { + if (rights & 0x01) + printf("%*cList directory\n",-level-8,marker); + if (rights & 0x02) + printf("%*cAdd file\n",-level-8,marker); + if (rights & 0x04) + printf("%*cAdd subdirectory\n",-level-8,marker); + if (rights & 0x08) + printf("%*cRead EA\n",-level-8,marker); + if (rights & 0x10) + printf("%*cWrite EA\n",-level-8,marker); + if (rights & 0x20) + printf("%*cTraverse\n",-level-8,marker); + if (rights & 0x40) + printf("%*cDelete child\n",-level-8,marker); + if (rights & 0x80) + printf("%*cRead attributes\n",-level-8,marker); + if (rights & 0x100) + printf("%*cWrite attributes\n",-level-8,marker); + } + else { + /* see FILE_READ_DATA etc in winnt.h */ + if (rights & 0x01) + printf("%*cRead data\n",-level-8,marker); + if (rights & 0x02) + printf("%*cWrite data\n",-level-8,marker); + if (rights & 0x04) + printf("%*cAppend data\n",-level-8,marker); + if (rights & 0x08) + printf("%*cRead EA\n",-level-8,marker); + if (rights & 0x10) + printf("%*cWrite EA\n",-level-8,marker); + if (rights & 0x20) + printf("%*cExecute\n",-level-8,marker); + if (rights & 0x80) + printf("%*cRead attributes\n",-level-8,marker); + if (rights & 0x100) + printf("%*cWrite attributes\n",-level-8,marker); + } + printf("%*cstandard acc rgts 0x%lx\n",-level-4,marker,(long)(rights >> 16) & 127); + if (rights & 0x10000) + printf("%*cDelete\n",-level-8,marker); + if (rights & 0x20000) + printf("%*cRead control\n",-level-8,marker); + if (rights & 0x40000) + printf("%*cWrite DAC\n",-level-8,marker); + if (rights & 0x80000) + printf("%*cWrite owner\n",-level-8,marker); + if (rights & 0x100000) + printf("%*cSynchronize\n",-level-8,marker); + if (rights & 0x800000) + printf("%*cCan access security ACL\n",-level-4,marker); + if (rights & 0x10000000) + printf("%*cGeneric all\n",-level-4,marker); + if (rights & 0x20000000) + printf("%*cGeneric execute\n",-level-4,marker); + if (rights & 0x40000000) + printf("%*cGeneric write\n",-level-4,marker); + if (rights & 0x80000000) + printf("%*cGeneric read\n",-level-4,marker); + + printf("%*cSID at 0x%x\n",-level,marker,off+8); + showsid(attr,off+8,"",level+4); + printf("%*cSummary :",-level,marker); + if (attr[off] == 0) + printf(" grant"); + if (attr[off] == 1) + printf(" deny"); + if (rights & le32_to_cpu(FILE_GREAD | FILE_GWRITE | FILE_GEXEC)) { + printf(" "); + if (rights & le32_to_cpu(FILE_GREAD)) + printf("r"); + if (rights & le32_to_cpu(FILE_GWRITE)) + printf("w"); + if (rights & le32_to_cpu(FILE_GEXEC)) + printf("x"); + } else + printf(" none"); + if (flags & 11) + printf(" inherited"); + if (!(flags & 8)) { + int sz; + + printf(" applied"); + sz = attr[off+9]*4 + 8; + if (!memcmp(&attr[off+8],&attr[get4l(attr,4)],sz)) + printf(" to owner"); + if (!memcmp(&attr[off+8],&attr[get4l(attr,8)],sz)) + printf(" to group"); + } + printf("\n"); + +} + +void showacl(const char *attr, int off, int isdir, int level) +{ + int i; + int cnt; + int size; + int x; + char marker; + + if (opt_b) + marker = '#'; + else + marker = ' '; + size = get2l(attr,off+2); + printf("%*crevision %d\n",-level,marker,attr[off]); + printf("%*cACL size %d\n",-level,marker,size); + cnt = get2l(attr,off+4); + printf("%*cACE cnt %d\n",-level,marker,cnt); + x = 8; + for (i=0; (i<cnt) && (x < size); i++) { + printf("%*cACE %d at 0x%x\n",-level,marker,i + 1,off+x); + showace(attr,off + x,isdir,level+4); + x += get2l(attr,off + x + 2); + } +} + +void showdacl(const char *attr, int isdir, int level) +{ + int off; + char marker; + + if (opt_b) + marker = '#'; + else + marker = ' '; + off = get4l(attr,16); + if (off) { + if (level) + printf("%*c",-level,marker); + printf("DACL\n"); + showacl(attr,off,isdir,level+4); + } else { + if (level) + printf("%*c",-level,marker); + printf("No DACL\n"); + } +} + +void showsacl(const char *attr, int isdir, int level) +{ + int off; + char marker; + + if (opt_b) + marker = '#'; + else + marker = ' '; + off = get4l(attr,12); + if (off) { + if (level) + printf("%*c",-level,marker); + printf("SACL\n"); + showacl(attr,off,isdir,level+4); + } + else { + if (level) + printf("%*c",-level,marker); + printf("No SACL\n"); + } +} + +void showall(const char *attr, int level) +{ + BOOL isdir; + + isdir = guess_dir(attr); + showheader(attr,level); + showusid(attr,level); + showgsid(attr,level); + showdacl(attr,isdir,level); + showsacl(attr,isdir,level); +} + +#if POSIXACLS +/* + * Display a Posix descriptor + */ + +void showposix(const struct POSIX_SECURITY *pxdesc) +{ + char txperm[4]; + const char *txtag; + const char *txtype; + const struct POSIX_ACL *acl; + const struct POSIX_ACE *pxace; + int acccnt; + int defcnt; + int firstdef; + int perms; + u16 tag; + s32 id; + int k,l; + + if (pxdesc) { + acccnt = pxdesc->acccnt; + defcnt = pxdesc->defcnt; + firstdef = pxdesc->firstdef; + acl = &pxdesc->acl; + printf("Posix descriptor :\n"); + printf(" acccnt %d\n",acccnt); + printf(" defcnt %d\n",defcnt); + printf(" firstdef %d\n",firstdef); + printf(" mode : 0%03o\n",(int)pxdesc->mode); + printf(" tagsset : 0x%02x\n",(int)pxdesc->tagsset); + printf("Posix ACL :\n"); + printf(" version %d\n",(int)acl->version); + printf(" flags 0x%02x\n",(int)acl->flags); + for (k=0; k<(acccnt + defcnt); k++) { + if (k < acccnt) + l = k; + else + l = firstdef + k - acccnt; + pxace = &acl->ace[l]; + tag = pxace->tag; + perms = pxace->perms; + if (tag == POSIX_ACL_SPECIAL) { + txperm[0] = (perms & S_ISVTX ? 's' : '-'); + txperm[1] = (perms & S_ISUID ? 'u' : '-'); + txperm[2] = (perms & S_ISGID ? 'g' : '-'); + } else { + txperm[0] = (perms & 4 ? 'r' : '-'); + txperm[1] = (perms & 2 ? 'w' : '-'); + txperm[2] = (perms & 1 ? 'x' : '-'); + } + txperm[3] = 0; + if (k >= acccnt) + txtype = "default"; + else + txtype = "access "; + switch (tag) { + case POSIX_ACL_USER : + txtag = "USER "; + break; + case POSIX_ACL_USER_OBJ : + txtag = "USR-O"; + break; + case POSIX_ACL_GROUP : + txtag = "GROUP"; + break; + case POSIX_ACL_GROUP_OBJ : + txtag = "GRP-O"; + break; + case POSIX_ACL_MASK : + txtag = "MASK "; + break; + case POSIX_ACL_OTHER : + txtag = "OTHER"; + break; + case POSIX_ACL_SPECIAL : + txtag = "SPECL"; + break; + default : + txtag = "UNKWN"; + break; + } + id = pxace->id; + printf("ace %d : %s %s %4ld perms 0%03o %s\n", + l,txtype,txtag,(long)id, + perms,txperm); + } + } else + printf("** NULL ACL\n"); +} + +#endif /* POSIXACLS */ + +#if defined(WIN32) | defined(STSC) + +#else + +/* + * Relay to get usid as defined during mounting + */ + +const SID *relay_find_usid(const struct MAPPING *usermapping __attribute__((unused)), + uid_t uid, SID *defusid) +{ + return (ntfs_get_usid(ntfs_context,uid,(char*)defusid) ? + defusid : (SID*)NULL); +} + +/* + * Relay to get gsid as defined during mounting + */ + +const SID *relay_find_gsid(const struct MAPPING *groupmapping __attribute__((unused)), + uid_t gid, SID *defgsid) +{ + return (ntfs_get_usid(ntfs_context,gid,(char*)defgsid) ? + defgsid : (SID*)NULL); +} + +/* + * Relay to get uid as defined during mounting + */ + +uid_t relay_find_user(const struct MAPPING *mapping __attribute__((unused)), + const SID *usid) +{ + int uid; + + uid = ntfs_get_user(ntfs_context,(const char*)usid); + return (uid < 0 ? 0 : uid); +} + +/* + * Relay to get gid as defined during mounting + */ + +gid_t relay_find_group(const struct MAPPING *mapping __attribute__((unused)), + const SID *gsid) +{ + int gid; + + gid = ntfs_get_group(ntfs_context,(const char*)gsid); + return (gid < 0 ? 0 : gid); +} + +#endif + +#if defined(WIN32) | defined(STSC) + +/* + * Dummy get uid from user name, out of a Linux environment + */ + +struct passwd *getpwnam(const char *user) +{ + ntfs_log_error("Cannot interpret id \"%s\"", user); + ntfs_log_error("please use numeric uids in UserMapping file\n"); + return ((struct passwd*)NULL); +} + +/* + * Dummy get gid from group name, out of a Linux environment + */ + +struct group *getgrnam(const char *group) +{ + ntfs_log_error("Cannot interpret id \"%s\"", group); + ntfs_log_error("please use numeric gids in UserMapping file\n"); + return ((struct group*)NULL); +} + +#endif /* defined(WIN32) | defined(STSC) */ + +#if POSIXACLS + +struct POSIX_SECURITY *linux_permissions_posix(const char *attr, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; +#if OWNERFROMACL + const SID *osid; +#endif + const SID *usid; + const SID *gsid; + struct POSIX_SECURITY *posix_desc; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + gsid = (const SID*)&attr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + osid = (const SID*)&attr[le32_to_cpu(phead->owner)]; + usid = ntfs_acl_owner((const char*)attr); +#if SELFTESTS & !USESTUBS + if (!opt_t && !ntfs_same_sid(usid,osid)) + printf("== Linux owner is different from Windows owner\n"); +#else + if (!ntfs_same_sid(usid,osid)) + printf("== Linux owner is different from Windows owner\n"); +#endif +#else + usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; +#endif + posix_desc = ntfs_build_permissions_posix(context.mapping, + (const char*)attr, usid, gsid, isdir); + if (!posix_desc) { + printf("** Failed to build a Posix descriptor\n"); + errors++; + } + return (posix_desc); +} + +#endif /* POSIXACLS */ + +int linux_permissions(const char *attr, BOOL isdir) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; +#if OWNERFROMACL + const SID *osid; +#endif + const SID *usid; + const SID *gsid; + int perm; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + gsid = (const SID*)&attr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + osid = (const SID*)&attr[le32_to_cpu(phead->owner)]; + usid = ntfs_acl_owner((const char*)attr); +#if SELFTESTS & !USESTUBS + if (!opt_t && !ntfs_same_sid(usid,osid)) + printf("== Linux owner is different from Windows owner\n"); +#else + if (!ntfs_same_sid(usid,osid)) + printf("== Linux owner is different from Windows owner\n"); +#endif +#else + usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; +#endif + perm = ntfs_build_permissions((const char*)attr, usid, gsid, isdir); + if (perm < 0) { + printf("** Failed to build permissions\n"); + errors++; + } + return (perm); +} + +uid_t linux_owner(const char *attr) +{ + const SID *usid; + uid_t uid; + +#if OWNERFROMACL + usid = ntfs_acl_owner((const char*)attr); +#else + const SECURITY_DESCRIPTOR_RELATIVE *phead; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; +#endif +#if defined(WIN32) | defined(STSC) + uid = ntfs_find_user(context.mapping[MAPUSERS],usid); +#else + if (mappingtype == MAPEXTERN) + uid = relay_find_user(context.mapping[MAPUSERS],usid); + else + uid = ntfs_find_user(context.mapping[MAPUSERS],usid); +#endif + return (uid); +} + +gid_t linux_group(const char *attr) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const SID *gsid; + gid_t gid; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + gsid = (const SID*)&attr[le32_to_cpu(phead->group)]; +#if defined(WIN32) | defined(STSC) + gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid); +#else + if (mappingtype == MAPEXTERN) + gid = relay_find_group(context.mapping[MAPGROUPS],gsid); + else + gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid); +#endif + return (gid); +} + +void newblock(s32 key) +{ + struct SECURITY_DATA *psecurdata; + int i; + + if ((key > 0) && (key < MAXSECURID) && !securdata[key >> SECBLKSZ]) { + securdata[key >> SECBLKSZ] = + (struct SECURITY_DATA*)malloc((1 << SECBLKSZ)*sizeof(struct SECURITY_DATA)); + if (securdata[key >> SECBLKSZ]) + for (i=0; i<(1 << SECBLKSZ); i++) { + psecurdata = &securdata[key >> SECBLKSZ][i]; + psecurdata->filecount = 0; + psecurdata->mode = 0; + psecurdata->flags = 0; + psecurdata->attr = (char*)NULL; + } + } +} + +void freeblocks(void) +{ + int i,j; + struct SECURITY_DATA *psecurdata; + + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + if (securdata[i]) { + for (j=0; j<(1 << SECBLKSZ); j++) { + psecurdata = &securdata[i][j]; + if (psecurdata->attr) + free(psecurdata->attr); + } + free(securdata[i]); + } +} + +/* + * Basic read from a user mapping file (Win32) + */ + +int basicread(void *fileid, char *buf, size_t size, + off_t pos __attribute__((unused))) +{ + return (read(*(int*)fileid, buf, size)); +} + +#if SELFTESTS & !USESTUBS + +/* + * Read a dummy mapping file for tests + */ + +int dummyread(void *fileid __attribute__((unused)), + char *buf, size_t size, off_t pos) +{ + size_t sz; + + if (pos >= (off_t)(sizeof(dummymapping) - 1)) + sz = 0; + else + if ((size + pos) >= (sizeof(dummymapping) - 1)) + sz = sizeof(dummymapping) - 1 - pos; + else + sz = size; + if (sz > 0) + memcpy(buf,&dummymapping[pos],sz); + return (sz); +} + +#endif /* POSIXACLS & SELFTESTS & !USESTUBS */ + +/* + * Apply default single user mapping + * returns zero if successful + */ + +static int do_default_mapping(struct MAPPING *mapping[], + const SID *usid) +{ + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + SID *sid; + int sidsz; + int res; + + res = -1; + sidsz = ntfs_sid_size(usid); +#if USESTUBS + sid = (SID*)stdmalloc(sidsz); /* will be freed within the library */ +#else + sid = (SID*)ntfs_malloc(sidsz); +#endif + if (sid) { + memcpy(sid,usid,sidsz); +#if USESTUBS + usermapping = (struct MAPPING*)stdmalloc(sizeof(struct MAPPING)); +#else + usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); +#endif + if (usermapping) { +#if USESTUBS + groupmapping = (struct MAPPING*)stdmalloc(sizeof(struct MAPPING)); +#else + groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); +#endif + if (groupmapping) { + usermapping->sid = sid; + usermapping->xid = 0; + usermapping->next = (struct MAPPING*)NULL; + groupmapping->sid = sid; + groupmapping->xid = 0; + groupmapping->next = (struct MAPPING*)NULL; + mapping[MAPUSERS] = usermapping; + mapping[MAPGROUPS] = groupmapping; + res = 0; + } + } + } + return (res); +} + +/* + * Build the user mapping + * - according to a mapping file if defined (or default present), + * - or try default single user mapping if possible + * + * The mapping is specific to a mounted device + * No locking done, mounting assumed non multithreaded + * + * returns zero if mapping is successful + * (failure should not be interpreted as an error) + */ + +int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path) +{ +#ifdef WIN32 + char mapfile[sizeof(MAPDIR) + sizeof(MAPFILE) + 6]; + char currpath[261]; +#else + char *mapfile; + char *p; +#endif + int fd; + struct MAPLIST *item; + struct MAPLIST *firstitem = (struct MAPLIST*)NULL; + struct MAPPING *usermapping; + struct MAPPING *groupmapping; + static struct { + u8 revision; + u8 levels; + be16 highbase; + be32 lowbase; + le32 level1; + le32 level2; + le32 level3; + le32 level4; + le32 level5; + } defmap = { + 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5), + const_cpu_to_le32(21), + const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2), + const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE) + } ; + + /* be sure not to map anything until done */ + mapping[MAPUSERS] = (struct MAPPING*)NULL; + mapping[MAPGROUPS] = (struct MAPPING*)NULL; + + if (usermap_path) { +#ifdef WIN32 +/* TODO : check whether the device can store acls */ + strcpy(mapfile,"x:\\" MAPDIR "\\" MAPFILE); + if (((const le16*)usermap_path)[1] == ':') + mapfile[0] = usermap_path[0]; + else { + GetModuleFileName(NULL, currpath, 261); + mapfile[0] = currpath[0]; + } + fd = open(mapfile,O_RDONLY); +#else + fd = 0; + mapfile = (char*)malloc(MAXFILENAME); + if (mapfile) { + /* build a full path to locate the mapping file */ + if ((usermap_path[0] != '/') + && getcwd(mapfile,MAXFILENAME)) { + strcat(mapfile,"/"); + strcat(mapfile,usermap_path); + } else + strcpy(mapfile,usermap_path); + p = strrchr(mapfile,'/'); + if (p) + do { + strcpy(p,"/" MAPDIR "/" MAPFILE); + fd = open(mapfile,O_RDONLY); + if (fd <= 0) { + *p = 0; + p = strrchr(mapfile,'/'); + if (p == mapfile) + p = (char*)NULL; + } + } while ((fd <= 0) && p); + free(mapfile); + if (!p) { + printf("** Could not find the user mapping file\n"); + if (usermap_path[0] != '/') + printf(" Retry with full path of file\n"); + errors++; + } + } +#endif + if (fd > 0) { + firstitem = ntfs_read_mapping(basicread, (void*)&fd); + close(fd); + } + } else { +#if SELFTESTS & !USESTUBS + firstitem = ntfs_read_mapping(dummyread, (void*)NULL); +#endif + } + + if (firstitem) { + usermapping = ntfs_do_user_mapping(firstitem); + groupmapping = ntfs_do_group_mapping(firstitem); + if (usermapping && groupmapping) { + mapping[MAPUSERS] = usermapping; + mapping[MAPGROUPS] = groupmapping; + } else + ntfs_log_error("There were no valid user or no valid group\n"); + /* now we can free the memory copy of input text */ + /* and rely on internal representation */ + while (firstitem) { + item = firstitem->next; +#if USESTUBS + stdfree(firstitem); /* allocated within library */ +#else + free(firstitem); +#endif + firstitem = item; + } + } else { + do_default_mapping(mapping,(const SID*)&defmap); + } + if (mapping[MAPUSERS]) + mappingtype = MAPLOCAL; + return (!mapping[MAPUSERS]); +} + +/* + * Get an hexadecimal number (source with MSB first) + */ + +u32 getmsbhex(const char *text) +{ + u32 v; + int b; + BOOL ok; + + v = 0; + ok = TRUE; + do { + b = *text++; + if ((b >= '0') && (b <= '9')) + v = (v << 4) + b - '0'; + else + if ((b >= 'a') && (b <= 'f')) + v = (v << 4) + b - 'a' + 10; + else + if ((b >= 'A') && (b <= 'F')) + v = (v << 4) + b - 'A' + 10; + else ok = FALSE; + } while (ok); + return (v); +} + + +/* + * Get an hexadecimal number (source with LSB first) + * An odd number of digits might yield a strange result + */ + +u32 getlsbhex(const char *text) +{ + u32 v; + int b; + BOOL ok; + int pos; + + v = 0; + ok = TRUE; + pos = 0; + do { + b = *text++; + if ((b >= '0') && (b <= '9')) + v |= (u32)(b - '0') << (pos ^ 4); + else + if ((b >= 'a') && (b <= 'f')) + v |= (u32)(b - 'a' + 10) << (pos ^ 4); + else + if ((b >= 'A') && (b <= 'F')) + v |= (u32)(b - 'A' + 10) << (pos ^ 4); + else ok = FALSE; + pos += 4; + } while (ok); + return (v); +} + + +/* + * Check whether a line looks like an hex dump + */ + +BOOL ishexdump(const char *line, int first, int lth) +{ + BOOL ok; + int i; + int b; + + ok = (first >= 0) && (lth >= (first + 16)); + for (i=0; ((first+i)<lth) && ok; i++) { + b = line[first + i]; + if ((i == 6) + || (i == 7) + || (i == 16) + || (i == 25) + || (i == 34) + || (i == 43)) + ok = (b == ' ') || (b == '\n'); + else + ok = ((b >= '0') && (b <= '9')) + || ((b >= 'a') && (b <= 'f')) + || ((b >= 'A') && (b <= 'F')); + } + return (ok); +} + + +/* + * Display security descriptors from a file + * This is typically to convert a verbose output to a very verbose one + */ + +void showhex(FILE *fd) +{ + static char attr[MAXATTRSZ]; + char line[MAXLINE+1]; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + int lth; + int first; + unsigned int pos; + u32 v; + int c; + int isdir; + int mode; + unsigned int off; + int i; + le32 *pattr; + BOOL isdump; + BOOL done; + + pos = 0; + off = 0; + done = FALSE; + do { + /* input a (partial) line without displaying */ + lth = 0; + first = -1; + do { + c = getc(fd); + if ((c != ' ') && (first < 0)) + first = lth; + if (c == EOF) + done = TRUE; + else + if (c != '\r') + line[lth++] = c; + } while (!done && (c != '\n') && (lth < MAXLINE)); + /* check whether this looks like an hexadecimal dump */ + isdump = ishexdump(line, first, lth); + if (isdump) off = getmsbhex(&line[first]); + /* line is not an hexadecimal dump */ + /* display what we have in store */ + if ((!isdump || !off) && pos && ntfs_valid_descr((char*)attr,pos)) { + printf(" Computed hash : 0x%08lx\n", + (unsigned long)hash((le32*)attr, + ntfs_attr_size(attr))); + isdir = guess_dir(attr); + printf(" Estimated type : %s\n",(isdir ? "directory" : "file")); + showheader(attr,4); + showusid(attr,4); + showgsid(attr,4); + showdacl(attr,isdir,4); + showsacl(attr,isdir,4); + showownership(attr); + mode = linux_permissions(attr,isdir); + printf("Interpreted Unix mode 0%03o\n",mode); +#if POSIXACLS + /* + * Posix display not possible when user + * mapping is not available (option -h) + */ + if (mappingtype != MAPNONE) { + pxdesc = linux_permissions_posix(attr,isdir); + if (pxdesc) { + showposix(pxdesc); + free(pxdesc); + } + } +#endif + pos = 0; + } + if (isdump && !off) + pos = off; + /* line looks like an hexadecimal dump */ + /* decode it into attribute */ + if (isdump && (off == pos)) { + for (i=first+8; i<lth; i+=9) { + pattr = (le32*)&attr[pos]; + v = getlsbhex(&line[i]); + *pattr = cpu_to_le32(v); + pos += 4; + } + } + /* display (full) current line */ + if (lth) printf("! "); + for (i=0; i<lth; i++) { + c = line[i]; + putchar(c); + } + while (!done && (c != '\n')) { + c = getc(fd); + if (c == EOF) + done = TRUE; + else + putchar(c); + } + } while (!done); +} + +BOOL applyattr(const char *fullname, const char *attr, + BOOL withattr, int attrib, s32 key) +{ + struct SECURITY_DATA *psecurdata; + const char *curattr; + char *newattr; + int selection; + BOOL bad; + BOOL badattrib; + BOOL err; +#ifdef WIN32 + HANDLE htoken; + TOKEN_PRIVILEGES tkp; +#endif + + err = FALSE; + psecurdata = (struct SECURITY_DATA*)NULL; + curattr = (const char*)NULL; + newattr = (char*)NULL; + if ((key > 0) && (key < MAXSECURID)) { + if (!securdata[key >> SECBLKSZ]) + newblock(key); + if (securdata[key >> SECBLKSZ]) { + psecurdata = &securdata[key >> SECBLKSZ] + [key & ((1 << SECBLKSZ) - 1)]; + } + } + + /* If we have a usable attrib value. Try applying */ + badattrib = FALSE; + if (opt_e && (attrib != INVALID_FILE_ATTRIBUTES)) { +#ifdef WIN32 + badattrib = !SetFileAttributesW((LPCWSTR)fullname, attrib); +#else + badattrib = !ntfs_set_file_attributes(ntfs_context, fullname, attrib); +#endif + if (badattrib) { + printf("** Could not set Windows attrib of "); + printname(stdout,fullname); + printf(" to 0x%x\n", attrib); + printerror(stdout); + warnings++; + } + } + + if (withattr) { + if (psecurdata) { + newattr = (char*)malloc(ntfs_attr_size(attr)); + if (newattr) { + memcpy(newattr,attr,ntfs_attr_size(attr)); + psecurdata->attr = newattr; + } + } + curattr = attr; + } else + /* + * No explicit attr in backup, use attr defined + * previously for the same id + */ + if (psecurdata) + curattr = psecurdata->attr; + + + if (curattr) { +#ifdef WIN32 + selection = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | DACL_SECURITY_INFORMATION; + if (OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &htoken)) { + if (LookupPrivilegeValue(NULL, SE_SECURITY_NAME, + &tkp.Privileges[0].Luid)) { + tkp.PrivilegeCount = 1; + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0)) { + selection |= SACL_SECURITY_INFORMATION; + } + } + } + /* const missing from stupid prototype */ + bad = !SetFileSecurityW((LPCWSTR)fullname, + selection, (PSECURITY_DESCRIPTOR)(LONG_PTR)curattr); + if (bad) + switch (GetLastError()) { + case 1307 : + case 1314 : + printf("** Could not set owner or SACL of "); + printname(stdout,fullname); + printf(", retrying with no owner or SACL setting\n"); + warnings++; + /* const missing from stupid prototype */ + bad = !SetFileSecurityW((LPCWSTR)fullname, + selection & ~OWNER_SECURITY_INFORMATION + & ~SACL_SECURITY_INFORMATION, + (PSECURITY_DESCRIPTOR) + (LONG_PTR)curattr); + break; + default : + break; + } + /* Release privileges once we are done*/ + if (selection ^ SACL_SECURITY_INFORMATION) { + tkp.Privileges[0].Attributes = 0; + AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0); + } +#else + selection = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | DACL_SECURITY_INFORMATION + | SACL_SECURITY_INFORMATION; + bad = !ntfs_set_file_security(ntfs_context,fullname, + selection, (const char*)curattr); +#endif + if (bad) { + printf("** Could not set the ACL of "); + printname(stdout,fullname); + printf("\n"); + printerror(stdout); + err = TRUE; + } else + if (opt_v) { + if (opt_e && !badattrib) + printf("ACL and attrib have been applied to "); + else + printf("ACL has been applied to "); + printname(stdout,fullname); + printf("\n"); + + } + } else { + printf("** There was no valid ACL for "); + printname(stdout,fullname); + printf("\n"); + err = TRUE; + } + return (!err); +} + +/* + * Restore security descriptors from a file + */ + +BOOL restore(FILE *fd) +{ + static char attr[MAXATTRSZ]; + char line[MAXFILENAME+25]; + char fullname[MAXFILENAME+25]; + SECURITY_DESCRIPTOR_RELATIVE *phead; + int lth; + int first; + unsigned int pos; + int c; + int isdir; + int mode; + s32 key; + BOOL isdump; + unsigned int off; + u32 v; + u32 oldhash; + int i; + int count; + int attrib; + le32 *pattr; + BOOL withattr; + BOOL done; + + pos = 0; + off = 0; + done = FALSE; + withattr = FALSE; + oldhash = 0; + key = 0; + errors = 0; + count = 0; + fullname[0] = 0; + attrib = INVALID_FILE_ATTRIBUTES; + do { + /* input a (partial) line without processing */ + lth = 0; + first = -1; + do { + c = getc(fd); + if ((c != ' ') && (first < 0)) + first = lth; + if (c == EOF) + done = TRUE; + else + if (c != '\r') + line[lth++] = c; + } while (!done && (c != '\n') && (lth < (MAXFILENAME + 24))); + /* check whether this looks like an hexadecimal dump */ + isdump = ishexdump(line, first, lth); + if (isdump) off = getmsbhex(&line[first]); + /* line is not an hexadecimal dump */ + /* apply what we have in store */ + if ((!isdump || !off) && pos && ntfs_valid_descr((char*)attr,pos)) { + withattr = TRUE; + if (opt_v >= 2) { + printf(" Computed hash : 0x%08lx\n", + (unsigned long)hash((le32*)attr, + ntfs_attr_size(attr))); + isdir = guess_dir(attr); + printf(" Estimated type : %s\n",(isdir ? "directory" : "file")); + showheader(attr,4); + showusid(attr,4); + showgsid(attr,4); + showdacl(attr,isdir,4); + showsacl(attr,isdir,4); + mode = linux_permissions(attr,isdir); + showownership(attr); + printf("Interpreted Unix mode 0%03o\n",mode); + } + pos = 0; + } + if (isdump && !off) + pos = off; + /* line looks like an hexadecimal dump */ + /* decode it into attribute */ + if (isdump && (off == pos)) { + for (i=first+8; i<lth; i+=9) { + pattr = (le32*)&attr[pos]; + v = getlsbhex(&line[i]); + *pattr = cpu_to_le32(v); + pos += 4; + } + } + /* display (full) current line unless dump or verbose */ + if (!isdump || opt_v) { + if(lth) printf("! "); + for (i=0; i<lth; i++) { + c = line[i]; + putchar(c); + } + } + while (!done && (c != '\n')) { + c = getc(fd); + if (c == EOF) + done = TRUE; + else + if (!isdump || opt_v) + putchar(c); + } + + line[lth] = 0; + while ((lth > 0) + && ((line[lth-1] == '\n') || (line[lth-1] == '\r'))) + line[--lth] = 0; + if (!strncmp(line,"Computed hash : 0x",18)) + oldhash = getmsbhex(&line[18]); + if (!strncmp(line,"Security key : 0x",17)) + key = getmsbhex(&line[17]); + if (!strncmp(line,"Windows attrib : 0x",19)) + attrib = getmsbhex(&line[19]); + if (done + || !strncmp(line,"File ",5) + || !strncmp(line,"Directory ",10)) { + /* + * New file or directory (or end of file) : + * apply attribute just collected + * or apply attribute defined from current key + */ + + if (withattr + && oldhash + && (hash((const le32*)attr,ntfs_attr_size(attr)) != oldhash)) { + printf("** ACL rejected, its hash is not as expected\n"); + errors++; + } else + if (fullname[0]) { + phead = (SECURITY_DESCRIPTOR_RELATIVE*)attr; + /* set the request for auto-inheritance */ + if (phead->control & SE_DACL_AUTO_INHERITED) + phead->control |= SE_DACL_AUTO_INHERIT_REQ; + if (!applyattr(fullname,attr,withattr, + attrib,key)) + errors++; + else + count++; + } + /* save current file or directory name */ + withattr = FALSE; + key = 0; + oldhash = 0; + attrib = INVALID_FILE_ATTRIBUTES; + if (!done) { +#ifdef WIN32 + if (!strncmp(line,"File ",5)) + makeutf16(fullname,&line[5]); + else + makeutf16(fullname,&line[10]); +#else + if (!strncmp(line,"File ",5)) + strcpy(fullname,&line[5]); + else + strcpy(fullname,&line[10]); +#endif + } + } + } while (!done); + printf("%d ACLs have been applied\n",count); + return (FALSE); +} + +/* + * Open the security API in rw mode for an ACL restoration + */ + +#ifdef WIN32 +#else +BOOL dorestore(const char *volume, FILE *fd) +{ + BOOL err; + + err = FALSE; + if (!getuid()) { + if (open_security_api()) { + if (open_volume(volume,NTFS_MNT_NONE)) { + if (restore(fd)) err = TRUE; + close_volume(volume); + } else { + fprintf(stderr,"Could not open volume %s\n",volume); + printerror(stderr); + err = TRUE; + } + close_security_api(); + } else { + fprintf(stderr,"Could not open security API\n"); + printerror(stderr); + err = TRUE; + } + } else { + fprintf(stderr,"Restore is only possible as root\n"); + err = TRUE; + } + return (err); +} +#endif /* WIN32 */ + +#if POSIXACLS & SELFTESTS & !USESTUBS + +/* + * Merge Posix ACL rights into an u32 (self test only) + * + * Result format : -----rwxrwxrwxrwxrwx---rwxrwxrwx + * U1 U2 G1 G2 M o g w + * + * Only two users (U1, U2) and two groups (G1, G2) taken into account + */ +u32 merge_rights(const struct POSIX_SECURITY *pxdesc, BOOL def) +{ + const struct POSIX_ACE *pxace; + int i; + int users; + int groups; + int first; + int last; + u32 rights; + + rights = 0; + users = 0; + groups = 0; + if (def) { + first = pxdesc->firstdef; + last = pxdesc->firstdef + pxdesc->defcnt - 1; + } else { + first = 0; + last = pxdesc->acccnt - 1; + } + pxace = pxdesc->acl.ace; + for (i=first; i<=last; i++) { + switch (pxace[i].tag) { + case POSIX_ACL_USER_OBJ : + rights |= (pxace[i].perms & 7) << 6; + break; + case POSIX_ACL_USER : + if (users < 2) + rights |= ((u32)pxace[i].perms & 7) << (24 - 3*users); + users++; + break; + case POSIX_ACL_GROUP_OBJ : + rights |= (pxace[i].perms & 7) << 3; + break; + case POSIX_ACL_GROUP : + if (groups < 2) + rights |= ((u32)pxace[i].perms & 7) << (18 - 3*groups); + groups++; + break; + case POSIX_ACL_MASK : + rights |= ((u32)pxace[i].perms & 7) << 12; + break; + case POSIX_ACL_OTHER : + rights |= (pxace[i].perms & 7); + break; + default : + break; + } + } + return (rights); +} + +void tryposix(struct POSIX_SECURITY *pxdesc) +{ + le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */ + { + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016) + } ; + le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */ + { + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513) + } ; + + char *attr; + BOOL isdir; + mode_t mode; + struct POSIX_SECURITY *newpxdesc; + struct POSIX_SECURITY *oldpxdesc; + static char *oldattr = (char*)NULL; + + isdir = FALSE; + if (oldattr) { + oldpxdesc = linux_permissions_posix(oldattr, isdir); + newpxdesc = ntfs_merge_descr_posix(pxdesc, oldpxdesc); + if (!newpxdesc) + newpxdesc = pxdesc; + free(oldpxdesc); + if (opt_v) { + printf("merged descriptors :\n"); + showposix(newpxdesc); + } + } else + newpxdesc = pxdesc; + attr = ntfs_build_descr_posix(context.mapping,newpxdesc, + isdir,(SID*)owner_sid,(SID*)group_sid); + if (attr && ntfs_valid_descr(attr, ntfs_attr_size(attr))) { + if (opt_v) + hexdump(attr,ntfs_attr_size(attr),8); + if (opt_v >= 2) { + showheader(attr,4); + showusid(attr,4); + showgsid(attr,4); + showdacl(attr,isdir,4); + showsacl(attr,isdir,4); + mode = linux_permissions(attr,isdir); + printf("Interpreted Unix mode 0%03o\n",mode); + printf("Interpreted back Posix descriptor :\n"); + newpxdesc = linux_permissions_posix(attr,isdir); + showposix(newpxdesc); + free(newpxdesc); + } + if (oldattr) free(oldattr); + oldattr = attr; + } +} + +static BOOL same_posix(struct POSIX_SECURITY *pxdesc1, + struct POSIX_SECURITY *pxdesc2) +{ + BOOL same; + int i; + + same = pxdesc1 + && pxdesc2 + && (pxdesc1->mode == pxdesc2->mode) + && (pxdesc1->acccnt == pxdesc2->acccnt) + && (pxdesc1->defcnt == pxdesc2->defcnt) + && (pxdesc1->firstdef == pxdesc2->firstdef) + && (pxdesc1->tagsset == pxdesc2->tagsset) + && (pxdesc1->acl.version == pxdesc2->acl.version) + && (pxdesc1->acl.flags == pxdesc2->acl.flags); + i = 0; + while (same && (i < pxdesc1->acccnt)) { + same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag) + && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms) + && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id); + i++; + } + i = pxdesc1->firstdef; + while (same && (i < pxdesc1->firstdef + pxdesc1->defcnt)) { + same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag) + && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms) + && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id); + i++; + } + return (same); +} + +#endif /* POSIXACLS & SELFTESTS & !USESTUBS */ + +#if SELFTESTS & !USESTUBS + +/* + * Build a dummy security descriptor + * returns descriptor in allocated memory, must free() after use + */ + +static char *build_dummy_descr(BOOL isdir __attribute__((unused)), + const SID *usid, const SID *gsid, + int cnt, + /* seq of int allow, SID *sid, int flags, u32 mask */ + ...) +{ + char *attr; + int attrsz; + SECURITY_DESCRIPTOR_RELATIVE *pnhead; + ACL *pacl; + ACCESS_ALLOWED_ACE *pace; + va_list ap; + const SID *sid; + u32 umask; + le32 mask; + int flags; + BOOL allow; + int pos; + int usidsz; + int gsidsz; + int sidsz; + int aclsz; + int i; + + if (usid) + usidsz = ntfs_sid_size(usid); + else + usidsz = 0; + if (gsid) + gsidsz = ntfs_sid_size(gsid); + else + gsidsz = 0; + + + /* allocate enough space for the new security attribute */ + attrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE) /* header */ + + usidsz + gsidsz /* usid and gsid */ + + sizeof(ACL) /* acl header */ + + cnt*40; + + attr = (char*)ntfs_malloc(attrsz); + if (attr) { + /* build the main header part */ + pnhead = (SECURITY_DESCRIPTOR_RELATIVE*) attr; + pnhead->revision = SECURITY_DESCRIPTOR_REVISION; + pnhead->alignment = 0; + /* + * The flag SE_DACL_PROTECTED prevents the ACL + * to be changed in an inheritance after creation + */ + pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED + | SE_SELF_RELATIVE; + /* + * Windows prefers ACL first, do the same to + * get the same hash value and avoid duplication + */ + /* build the ACL header */ + pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); + pacl = (ACL*)&attr[pos]; + pacl->revision = ACL_REVISION; + pacl->alignment1 = 0; + pacl->size = cpu_to_le16(0); /* fixed later */ + pacl->ace_count = cpu_to_le16(cnt); + pacl->alignment2 = cpu_to_le16(0); + + /* enter the ACEs */ + + pos += sizeof(ACL); + aclsz = sizeof(ACL); + va_start(ap,cnt); + for (i=0; i<cnt; i++) { + pace = (ACCESS_ALLOWED_ACE*)&attr[pos]; + allow = va_arg(ap,int); + sid = va_arg(ap,SID*); + flags = va_arg(ap,int); + umask = va_arg(ap,u32); + mask = cpu_to_le32(umask); + sidsz = ntfs_sid_size(sid); + pace->type = (allow ? ACCESS_ALLOWED_ACE_TYPE : ACCESS_DENIED_ACE_TYPE); + pace->flags = flags; + pace->size = cpu_to_le16(sidsz + 8); + pace->mask = mask; + memcpy(&pace->sid,sid,sidsz); + aclsz += sidsz + 8; + pos += sidsz + 8; + } + va_end(ap); + + /* append usid and gsid if defined */ + /* positions of ACL, USID and GSID into header */ + pnhead->owner = cpu_to_le32(0); + pnhead->group = cpu_to_le32(0); + if (usid) { + memcpy(&attr[pos], usid, usidsz); + pnhead->owner = cpu_to_le32(pos); + } + if (gsid) { + memcpy(&attr[pos + usidsz], gsid, gsidsz); + pnhead->group = cpu_to_le32(pos + usidsz); + } + /* positions of DACL and SACL into header */ + pnhead->sacl = cpu_to_le32(0); + if (cnt) { + pacl->size = cpu_to_le16(aclsz); + pnhead->dacl = + cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); + } else + pnhead->dacl = cpu_to_le32(0); + if (!ntfs_valid_descr(attr,pos+usidsz+gsidsz)) { + printf("** Bad sample descriptor\n"); + free(attr); + attr = (char*)NULL; + errors++; + } + } else + errno = ENOMEM; + return (attr); +} + +/* + * Check a few samples with special conditions + */ + +void check_samples() +{ + char *descr = (char*)NULL; + BOOL isdir = FALSE; + mode_t perms; + mode_t expect = 0; + int cnt; + u32 expectacc; + u32 expectdef; +#if POSIXACLS + u32 accrights; + u32 defrights; + mode_t mixmode; + struct POSIX_SECURITY *pxdesc; + struct POSIX_SECURITY *pxsample; + const char *txsample; +#endif + le32 owner1[] = /* S-1-5-21-1833069642-4243175381-1340018762-1003 */ + { + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(1833069642), cpu_to_le32(4243175381), + cpu_to_le32(1340018762), cpu_to_le32(1003) + } ; + le32 group1[] = /* S-1-5-21-1833069642-4243175381-1340018762-513 */ + { + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(1833069642), cpu_to_le32(4243175381), + cpu_to_le32(1340018762), cpu_to_le32(513) + } ; + le32 group2[] = /* S-1-5-21-1607551490-981732888-1819828000-513 */ + { + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(1607551490), cpu_to_le32(981732888), + cpu_to_le32(1819828000), cpu_to_le32(513) + } ; + le32 owner3[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */ + { + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016) + } ; + le32 group3[] = /* S-1-5-21-3141592653-589793238-462843383-513 */ + { + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513) + } ; + +#if POSIXACLS + struct { + struct POSIX_SECURITY head; + struct POSIX_ACE ace[4]; + } sampletry1 = + { + { 0645, 4, 0, 4, 0x35, + { POSIX_VERSION, 0, 0 } + }, + { + { 1, 6, -1 }, + { 4, 5, -1 }, + { 16, 4, -1 }, + { 32, 5, -1 } + } + } ; + + struct { + struct POSIX_SECURITY head; + struct POSIX_ACE ace[6]; + } sampletry3 = + { + { 0100, 6, 0, 6, 0x3f, + { POSIX_VERSION, 0, 0 } + }, + { + { 1, 1, -1 }, + { 2, 3, 1000 }, + { 4, 1, -1 }, + { 8, 3, 1002 }, + { 16, 0, -1 }, + { 32, 0, -1 } + } + } ; + + struct { + struct POSIX_SECURITY head; + struct POSIX_ACE ace[8]; + } sampletry4 = + { + { 0140, 8, 0, 8, 0x3f, + { POSIX_VERSION, 0, 0 } + }, + { + { 1, 1, -1 }, + { 2, 3, 516 }, + { 2, 6, 1000 }, + { 4, 1, -1 }, + { 8, 6, 500 }, + { 8, 3, 1002 }, + { 16, 4, -1 }, + { 32, 0, -1 } + } + } ; + + struct { + struct POSIX_SECURITY head; + struct POSIX_ACE ace[6]; + } sampletry5 = + { + { 0454, 6, 0, 6, 0x3f, + { POSIX_VERSION, 0, 0 } + }, + { + { 1, 4, -1 }, + { 2, 5, 516 }, + { 4, 4, -1 }, + { 8, 6, 500 }, + { 16, 5, -1 }, + { 32, 4, -1 } + } + } ; + + struct { + struct POSIX_SECURITY head; + struct POSIX_ACE ace[8]; + } sampletry6 = + { + { 0332, 8, 0, 8, 0x3f, + { POSIX_VERSION, 0, 0 } + }, + { + { 1, 3, -1 }, + { 2, 1, 0 }, + { 2, 2, 1000 }, + { 4, 6, -1 }, + { 8, 4, 0 }, + { 8, 5, 1002 }, + { 16, 3, -1 }, + { 32, 2, -1 } + } + } ; + + struct { + struct POSIX_SECURITY head; + struct POSIX_ACE ace[4]; + } sampletry8 = + { + { 0677, 4, 0, 4, 0x35, + { POSIX_VERSION, 0, 0 } + }, + { + { 1, 6, -1 }, + { 4, 7, -1 }, + { 16, 7, -1 }, + { 32, 7, -1 } + } + } ; + +#endif /* POSIXACLS */ + + +#if POSIXACLS + for (cnt=1; cnt<=8; cnt++) { + switch (cnt) { + case 1 : + pxsample = &sampletry1.head; + txsample = "sampletry1-a"; + isdir = FALSE; + descr = ntfs_build_descr_posix(context.mapping,&sampletry1.head, + isdir, (const SID*)owner3, (const SID*)group3); + break; + case 2 : + pxsample = &sampletry1.head; + txsample = "sampletry1-b"; + isdir = FALSE; + descr = ntfs_build_descr_posix(context.mapping,&sampletry1.head, + isdir, (const SID*)adminsid, (const SID*)group3); + break; + case 3 : + isdir = FALSE; + pxsample = &sampletry3.head; + txsample = "sampletry3"; + descr = ntfs_build_descr_posix(context.mapping,pxsample, + isdir, (const SID*)group3, (const SID*)group3); + break; + case 4 : + isdir = FALSE; + pxsample = &sampletry4.head; + txsample = "sampletry4"; + descr = ntfs_build_descr_posix(context.mapping,pxsample, + isdir, (const SID*)owner3, (const SID*)group3); + break; + case 5 : + isdir = FALSE; + pxsample = &sampletry5.head; + txsample = "sampletry5"; + descr = ntfs_build_descr_posix(context.mapping,pxsample, + isdir, (const SID*)owner3, (const SID*)group3); + break; + case 6 : + isdir = FALSE; + pxsample = &sampletry6.head; + txsample = "sampletry6-a"; + descr = ntfs_build_descr_posix(context.mapping,pxsample, + isdir, (const SID*)owner3, (const SID*)group3); + break; + case 7 : + isdir = FALSE; + pxsample = &sampletry6.head; + txsample = "sampletry6-b"; + descr = ntfs_build_descr_posix(context.mapping,pxsample, + isdir, (const SID*)adminsid, (const SID*)adminsid); + break; + case 8 : + pxsample = &sampletry8.head; + txsample = "sampletry8"; + isdir = FALSE; + descr = ntfs_build_descr_posix(context.mapping,&sampletry8.head, + isdir, (const SID*)owner3, (const SID*)group3); + break; + default : + pxsample = (struct POSIX_SECURITY*)NULL; + txsample = (const char*)NULL; + } + /* check we get original back */ + if (descr) + pxdesc = linux_permissions_posix(descr, isdir); + else + pxdesc = (struct POSIX_SECURITY*)NULL; + if (!descr || !pxdesc || !same_posix(pxsample,pxdesc)) { + printf("** Error in %s\n",txsample); + showposix(pxsample); + showall(descr,0); + showposix(pxdesc); + errors++; + } + free(descr); + free(pxdesc); + } + +#endif /* POSIXACLS */ + + + /* + * Check a few samples built by Windows, + * which cannot be generated by Linux + */ + + for (cnt=1; cnt<=10; cnt++) { + switch(cnt) { + case 1 : /* hp/tmp */ + isdir = TRUE; + descr = build_dummy_descr(isdir, + (const SID*)owner1, (const SID*)group1, + 1, + (int)TRUE, worldsid, (int)0x3, (u32)0x1f01ff); + expectacc = expect = 0777; + expectdef = 0; + break; + case 2 : /* swsetup */ + isdir = TRUE; + descr = build_dummy_descr(isdir, adminsid, (const SID*)group2, + 2, + (int)TRUE, worldsid, (int)0x0, (u32)0x1f01ff, + (int)TRUE, worldsid, (int)0xb, (u32)0x1f01ff); + expectacc = expect = 0777; + expectdef = 0777; + break; + case 3 : /* Dr Watson */ + isdir = TRUE; + descr = build_dummy_descr(isdir, (const SID*)owner3, (const SID*)group3, + 0); + expectacc = expect = 0700; + expectdef = 0; + break; + case 4 : + isdir = FALSE; + descr = build_dummy_descr(isdir, + (const SID*)owner3, (const SID*)group3, + 4, + (int)TRUE, (const SID*)owner3, 0, + le32_to_cpu(FILE_READ_DATA | OWNER_RIGHTS), + (int)TRUE, (const SID*)group3, 0, + le32_to_cpu(FILE_WRITE_DATA), + (int)TRUE, (const SID*)group2, 0, + le32_to_cpu(FILE_WRITE_DATA | FILE_READ_DATA), + (int)TRUE, (const SID*)worldsid, 0, + le32_to_cpu(FILE_EXECUTE)); + expect = 0731; + expectacc = 07070731; + expectdef = 0; + break; + case 5 : /* Vista/JP */ + isdir = TRUE; + descr = build_dummy_descr(isdir, systemsid, systemsid, + 6, + (int)TRUE, owner1, (int)0x0, (u32)0x1f01ff, + (int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff, + (int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff, + (int)TRUE, owner1, (int)0xb, (u32)0x10000000, + (int)TRUE, systemsid, (int)0xb, (u32)0x10000000, + (int)TRUE, adminsid, (int)0xb, (u32)0x10000000); + expectacc = expect = 0700; + expectdef = 0700; + break; + case 6 : /* Vista/JP2 */ + isdir = TRUE; + descr = build_dummy_descr(isdir, systemsid, systemsid, + 7, + (int)TRUE, owner1, (int)0x0, (u32)0x1f01ff, + (int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff, + (int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff, + (int)TRUE, owner1, (int)0xb, (u32)0x1f01ff, + (int)TRUE, systemsid, (int)0xb, (u32)0x1f01ff, + (int)TRUE, adminsid, (int)0xb, (u32)0x1f01ff, + (int)TRUE, owner3, (int)0x3, (u32)0x1200a9); + expectacc = 0500070700; + expectdef = 0700; + expect = 0700; + break; + case 7 : /* WinXP/JP */ + isdir = TRUE; + descr = build_dummy_descr(isdir, adminsid, systemsid, + 6, + (int)TRUE, owner1, (int)0x0, (u32)0x1f01ff, + (int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff, + (int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff, + (int)TRUE, owner1, (int)0xb, (u32)0x10000000, + (int)TRUE, systemsid, (int)0xb, (u32)0x10000000, + (int)TRUE, adminsid, (int)0xb, (u32)0x10000000); + expectacc = expect = 0700; + expectdef = 0700; + break; + case 8 : /* WinXP/JP2 */ + isdir = TRUE; + descr = build_dummy_descr(isdir, adminsid, systemsid, + 6, + (int)TRUE, owner1, (int)0x0, (u32)0x1f01ff, + (int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff, + (int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff, + (int)TRUE, owner1, (int)0xb, (u32)0x10000000, + (int)TRUE, systemsid, (int)0xb, (u32)0x10000000, + (int)TRUE, adminsid, (int)0xb, (u32)0x10000000); + expectacc = expect = 0700; + expectdef = 0700; + break; + case 9 : /* Win8/bin */ + isdir = TRUE; + descr = build_dummy_descr(isdir, + (const SID*)owner3, (const SID*)owner3, + 6, + (int)TRUE, authsid, (int)0x3, (u32)0x1f01ff, + (int)TRUE, adminsid, (int)0x13, (u32)0x1f01ff, + (int)TRUE, systemsid, (int)0x13, (u32)0x1f01ff, + (int)TRUE, localsid, (int)0x13, (u32)0x1200a9, + (int)TRUE, authsid, (int)0x10, (u32)0x1301bf, + (int)TRUE, authsid, (int)0x1b, (u32)0xe0010000); + expectacc = expect = 0777; + expectdef = 0777; + break; + case 10 : /* Win8/bin/linem.exe */ + isdir = FALSE; + descr = build_dummy_descr(isdir, + (const SID*)owner3, (const SID*)owner3, + 4, + (int)TRUE, authsid, (int)0x10, (u32)0x1f01ff, + (int)TRUE, adminsid, (int)0x10, (u32)0x1f01ff, + (int)TRUE, systemsid, (int)0x10, (u32)0x1ff, + (int)TRUE, localsid, (int)0x10, (u32)0x1200a9); + expectacc = expect = 0777; + expectdef = 0; + break; + default : + expectacc = expectdef = 0; + break; + } + if (descr) { + perms = linux_permissions(descr, isdir); + if (perms != expect) { + printf("** Error in sample %d, perms 0%03o expected 0%03o\n", + cnt,perms,expect); + showall(descr,0); + errors++; + } else { +#if POSIXACLS + pxdesc = linux_permissions_posix(descr, isdir); + if (pxdesc) { + accrights = merge_rights(pxdesc,FALSE); + defrights = merge_rights(pxdesc,TRUE); + if (!(pxdesc->tagsset & ~(POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER))) + mixmode = expect; + else + mixmode = (expect & 07707) | ((accrights >> 9) & 070); + if ((pxdesc->mode != mixmode) + || (accrights != expectacc) + || (defrights != expectdef)) { + printf("** Error in sample %d : mode %03o expected 0%03o\n", + cnt,pxdesc->mode,mixmode); + printf(" Posix access rights 0%03lo expected 0%03lo\n", + (long)accrights,(long)expectacc); + printf(" default rights 0%03lo expected 0%03lo\n", + (long)defrights,(long)expectdef); + showall(descr,0); + showposix(pxdesc); +exit(1); + } + free(pxdesc); + } +#endif + } + free(descr); + } + } +} + + +/* + * Check whether any basic permission setting is interpreted + * back exactly as set + */ + +void basictest(int kind, BOOL isdir, const SID *owner, const SID *group) +{ + char *attr; + mode_t perm; + mode_t gotback; + u32 count; + u32 acecount; + u32 globhash; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + enum { ERRNO, + ERRMA, ERRPA, /* error converting mode or Posix ACL to NTFS */ + ERRAM, ERRAP, /* error converting NTFS to mode or Posix ACL */ + } err; + u32 expectcnt[] = { + 27800, 31896, + 24064, 28160, + 24064, 28160, + 24064, 28160, + 25416, 29512 + } ; + u32 expecthash[] = { + 0x8f80865b, 0x7bc7960, + 0x8fd9ecfe, 0xddd4db0, + 0xa8b07400, 0xa189c20, + 0xc5689a00, 0xb6c09000, + 0x94bfb419, 0xa4311791 + } ; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; + char *pxattr; + u32 pxcount; + u32 pxacecount; + u32 pxglobhash; +#endif + + count = 0; + acecount = 0; + globhash = 0; +#if POSIXACLS + pxcount = 0; + pxacecount = 0; + pxglobhash = 0; +#endif + for (perm=0; (perm<=07777) && (errors < 10); perm++) { + err = ERRNO; + /* file owned by plain user and group */ + attr = ntfs_build_descr(perm,isdir,owner,(const SID*)group); + if (attr && ntfs_valid_descr(attr, ntfs_attr_size(attr))) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + pacl = (const ACL*)&attr[le32_to_cpu(phead->dacl)]; + acecount += le16_to_cpu(pacl->ace_count); + globhash += hash((const le32*)attr,ntfs_attr_size(attr)); + count++; +#if POSIXACLS + /* + * Build a NTFS ACL from a mode, and + * decode to a Posix ACL, expecting to + * get the original mode back. + */ + pxdesc = linux_permissions_posix(attr, isdir); + if (!pxdesc || (pxdesc->mode != perm)) { + err = ERRAP; + if (pxdesc) + gotback = pxdesc->mode; + else + gotback = 0; + } else { + /* + * Build a NTFS ACL from the Posix ACL, expecting to + * get exactly the same NTFS ACL, then decode to a + * mode, expecting to get the original mode back. + */ + pxattr = ntfs_build_descr_posix(context.mapping, + pxdesc,isdir,owner, + (const SID*)group); + if (pxattr && !memcmp(pxattr,attr, + ntfs_attr_size(attr))) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + pacl = (const ACL*)&attr[le32_to_cpu(phead->dacl)]; + pxacecount += le16_to_cpu(pacl->ace_count); + pxglobhash += hash((const le32*)attr,ntfs_attr_size(attr)); + pxcount++; + gotback = linux_permissions(pxattr, isdir); + if (gotback != perm) + err = ERRAM; + else + free(pxattr); + } else + err = ERRPA; + free(attr); + } + free(pxdesc); +#else + gotback = linux_permissions(attr, isdir); + if (gotback != perm) + err = ERRAM; + else + free(attr); +#endif /* POSIXACLS */ + } else + err = ERRMA; + + switch (err) { + case ERRMA : + printf("** no or wrong permission settings " + "for kind %d perm %03o\n",kind,perm); + if (attr && opt_v) + hexdump(attr,ntfs_attr_size(attr),8); + if (attr && (opt_v >= 2)) { + showheader(attr,4); + showusid(attr,4); + showgsid(attr,4); + showdacl(attr,isdir,4); + showsacl(attr,isdir,4); + } + errors++; + break; + case ERRPA : + printf("** no or wrong permission settings from PX " + "for kind %d perm %03o\n",kind,perm); + errors++; + break; +#if POSIXACLS + case ERRAM : + printf("** wrong permission settings, " + "kind %d perm 0%03o, gotback %03o\n", + kind, perm, gotback); + if (opt_v) + hexdump(pxattr,ntfs_attr_size(pxattr),8); + if (opt_v >= 2) { + showheader(pxattr,4); + showusid(pxattr,4); + showgsid(pxattr,4); + showdacl(pxattr,isdir,4); + showsacl(pxattr,isdir,4); + } + errors++; + break; + case ERRAP : + /* continued */ +#else + case ERRAM : + case ERRAP : +#endif /* POSIXACLS */ + printf("** wrong permission settings, " + "kind %d perm 0%03o, gotback %03o\n", + kind, perm, gotback); + if (opt_v) + hexdump(attr,ntfs_attr_size(attr),8); + if (opt_v >= 2) { + showheader(attr,4); + showusid(attr,4); + showgsid(attr,4); + showdacl(attr,isdir,4); + showsacl(attr,isdir,4); + } + errors++; + free(attr); + break; + default : + break; + } + } + printf("%lu ACLs built from mode, %lu ACE built, mean count %lu.%02lu\n", + (unsigned long)count,(unsigned long)acecount, + (unsigned long)acecount/count,acecount*100L/count%100L); + if (acecount != expectcnt[kind]) { + printf("** Error : expected ACE count %lu\n", + (unsigned long)expectcnt[kind]); + errors++; + } + if (globhash != expecthash[kind]) { + printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n", + (unsigned long)globhash, (unsigned long)expecthash[kind]); + errors++; + } +#if POSIXACLS + printf("%lu ACLs built from Posix ACLs, %lu ACE built, mean count %lu.%02lu\n", + (unsigned long)pxcount,(unsigned long)pxacecount, + (unsigned long)pxacecount/pxcount,pxacecount*100L/pxcount%100L); + if (pxacecount != expectcnt[kind]) { + printf("** Error : expected ACE count %lu\n", + (unsigned long)expectcnt[kind]); + errors++; + } + if (pxglobhash != expecthash[kind]) { + printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n", + (unsigned long)pxglobhash, (unsigned long)expecthash[kind]); + errors++; + } +#endif /* POSIXACLS */ +} + +#if POSIXACLS + +/* + * Check whether Posix ACL settings are interpreted + * back exactly as set + */ + +void posixtest(int kind, BOOL isdir, + const SID *owner, const SID *group) +{ + struct POSIX_SECURITY *pxdesc; + struct { + struct POSIX_SECURITY pxdesc; + struct POSIX_ACE aces[10]; + } desc; + int ownobj; + int grpobj; + int usr; + int grp; + int wrld; + int mask; + int mindes, maxdes; + int minmsk, maxmsk; + char *pxattr; + u32 count; + u32 acecount; + u32 globhash; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const ACL *pacl; + struct POSIX_SECURITY *gotback; + enum { ERRNO, + ERRMA, ERRPA, /* error converting mode or Posix ACL to NTFS */ + ERRAM, ERRAP, /* error converting NTFS to mode or Posix ACL */ + } err; + u32 expectcnt[] = { +#ifdef STSC + 32400, 34992, + 25920, 28512, + 25920, 28512, + 25920, 28512, + 26460, 29052, + 0, 0, + 0, 0, + 0, 0, + 24516, 27108, + 20736, 23328, + 20736, 23328, + 20736, 23328, + 21060, 23652, +#else + 252720, 273456, + 199584, 220320, + 199584, 220320, + 199584, 220320, + 203904, 224640, + 0, 0, + 0, 0, + 0, 0, + 196452, 217188, + 165888, 186624, + 165888, 186624, + 165888, 186624, + 168480, 189216, +#endif + 0, 0, + 0, 0, + 0, 0, + 16368, 18672, + 0, 0, + 13824, 0, + 0, 0, + 14640, 0 + } ; + u32 expecthash[] = { +#ifdef STSC + 0xf9f82115, 0x40666647, + 0xde826d30, 0xa181b660, + 0x952d4500, 0x8ac49450, + 0xf80acee0, 0xbd9ec6c0, + 0xfe09b868, 0xde24e84d, + 0, 0, + 0, 0, + 0, 0, + 0x2381438d, 0x3ab42dc6, + 0x7cccf6f8, 0x108ad430, + 0x5e448840, 0x83ab6c40, + 0x9b037100, 0x8f7c3b40, + 0x04a359dc, 0xa4619609, +#else + 0x1808a6cd, 0xd82f7c60, + 0x5ad29e85, 0x518c7620, + 0x188ce270, 0x7e44e590, + 0x48a64800, 0x5bdf0030, + 0x1c64aec6, 0x8b0168fa, + 0, 0, + 0, 0, + 0, 0, + 0x169fb80e, 0x382d9a59, + 0xf9c28164, 0x1855d352, + 0xf9685700, 0x44d16700, + 0x587ebe90, 0xf7c51480, + 0x2cb1b518, 0x52408df6, +#endif + 0, 0, + 0, 0, + 0, 0, + 0x905f2e38, 0xd40c22f0, + 0, 0, + 0xdd76da00, 0, + 0, 0, + 0x718e34a0, 0 + }; + + count = 0; + acecount = 0; + globhash = 0; + /* fill headers */ + pxdesc = &desc.pxdesc; + pxdesc->mode = 0; + pxdesc->defcnt = 0; + if (kind & 32) { + pxdesc->acccnt = 4; + pxdesc->firstdef = 4; + pxdesc->tagsset = 0x35; + } else { + pxdesc->acccnt = 6;; + pxdesc->firstdef = 6; + pxdesc->tagsset = 0x3f; + } + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + /* prefill aces */ + pxdesc->acl.ace[0].tag = POSIX_ACL_USER_OBJ; + pxdesc->acl.ace[0].id = -1; + if (kind & 32) { + pxdesc->acl.ace[1].tag = POSIX_ACL_GROUP_OBJ; + pxdesc->acl.ace[1].id = -1; + pxdesc->acl.ace[2].tag = POSIX_ACL_MASK; + pxdesc->acl.ace[2].id = -1; + pxdesc->acl.ace[3].tag = POSIX_ACL_OTHER; + pxdesc->acl.ace[3].id = -1; + } else { + pxdesc->acl.ace[1].tag = POSIX_ACL_USER; + pxdesc->acl.ace[1].id = (kind & 16 ? 0 : 1000); + pxdesc->acl.ace[2].tag = POSIX_ACL_GROUP_OBJ; + pxdesc->acl.ace[2].id = -1; + pxdesc->acl.ace[3].tag = POSIX_ACL_GROUP; + pxdesc->acl.ace[3].id = (kind & 16 ? 0 : 1002); + pxdesc->acl.ace[4].tag = POSIX_ACL_MASK; + pxdesc->acl.ace[4].id = -1; + pxdesc->acl.ace[5].tag = POSIX_ACL_OTHER; + pxdesc->acl.ace[5].id = -1; + } + + mindes = 3; + maxdes = (kind & 32 ? mindes : 6); +#ifdef STSC + minmsk = (kind & 32 ? 0 : 3); + maxmsk = (kind & 32 ? 7 : 3); +#else + minmsk = 0; + maxmsk = 7; +#endif + for (mask=minmsk; mask<=maxmsk; mask++) + for (ownobj=1; ownobj<7; ownobj++) + for (grpobj=1; grpobj<7; grpobj++) + for (wrld=0; wrld<8; wrld++) + for (usr=mindes; usr<=maxdes; usr++) + if (usr != 4) + for (grp=mindes; grp<=maxdes; grp++) + if (grp != 4) { + pxdesc->mode = (ownobj << 6) | (mask << 3) | wrld; + + pxdesc->acl.ace[0].perms = ownobj; + if (kind & 32) { + pxdesc->acl.ace[1].perms = grpobj; + pxdesc->acl.ace[2].perms = mask; + pxdesc->acl.ace[3].perms = wrld; + } else { + pxdesc->acl.ace[1].perms = usr; + pxdesc->acl.ace[2].perms = grpobj; + pxdesc->acl.ace[3].perms = grp; + pxdesc->acl.ace[4].perms = mask; + pxdesc->acl.ace[5].perms = wrld; + } + + err = ERRNO; + gotback = (struct POSIX_SECURITY*)NULL; + pxattr = ntfs_build_descr_posix(context.mapping, + pxdesc,isdir,owner,group); + if (pxattr && ntfs_valid_descr(pxattr, ntfs_attr_size(pxattr))) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)pxattr; + pacl = (const ACL*)&pxattr[le32_to_cpu(phead->dacl)]; + acecount += le16_to_cpu(pacl->ace_count); + globhash += hash((const le32*)pxattr,ntfs_attr_size(pxattr)); + count++; + gotback = linux_permissions_posix(pxattr, isdir); + if (gotback) { + if (ntfs_valid_posix(gotback)) { + if (!same_posix(pxdesc,gotback)) { + printf("Non matching got back Posix ACL\n"); + printf("input ACL\n"); + showposix(pxdesc); + printf("NTFS owner\n"); + showusid(pxattr,4); + printf("NTFS group\n"); + showgsid(pxattr,4); + printf("NTFS DACL\n"); + showdacl(pxattr,isdir,4); + printf("gotback ACL\n"); + showposix(gotback); + errors++; +exit(1); + } + } else { + printf("Got back an invalid Posix ACL\n"); + errors++; + } + free(gotback); + } else { + printf("Could not get Posix ACL back\n"); + errors++; + } + + } else { + printf("NTFS ACL incorrect or not build\n"); + printf("input ACL\n"); + showposix(pxdesc); + printf("NTFS DACL\n"); + if (pxattr) + showdacl(pxattr,isdir,4); + else + printf(" (none)\n"); + if (gotback) { + printf("gotback ACL\n"); + showposix(gotback); + } else + printf("no gotback ACL\n"); + errors++; + } + if (pxattr) + free(pxattr); + } + printf("%lu ACLs built from Posix ACLs, %lu ACE built, mean count %lu.%02lu\n", + (unsigned long)count,(unsigned long)acecount, + (unsigned long)acecount/count,acecount*100L/count%100L); + if (acecount != expectcnt[kind]) { + printf("** Error ! expected ACE count %lu\n", + (unsigned long)expectcnt[kind]); + errors++; + } + if (globhash != expecthash[kind]) { + printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n", + (unsigned long)globhash, (unsigned long)expecthash[kind]); + errors++; + } +} + +#endif /* POSIXACLS */ + +void selftests(void) +{ + le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */ + { + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016) + } ; + le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */ + { + cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21), + cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2), + cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513) + } ; +#if POSIXACLS +#ifdef STSC + unsigned char kindlist[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 16, 17, 18, 20, 22, 24, + 32, 33, 36, 40 } ; +#else + unsigned char kindlist[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 16, 17, 18, 20, 22, 24, 19, 21, 23, 25, + 32, 33, 36, 40 } ; +#endif + unsigned int k; +#endif /* POSIXACLS */ + int kind; + const SID *owner; + const SID *group; + BOOL isdir; + +#if POSIXACLS + local_build_mapping(context.mapping, (const char*)NULL); +#endif + /* first check samples */ + mappingtype = MAPDUMMY; + check_samples(); +if (errors) exit(1); + /* + * kind is oring of : + * 1 : directory + * 2 : owner is root + * 4 : group is root + * 8 : group is owner + * 16 : root is designated user/group + * 32 : mask present with no designated user/group + */ + for (kind=0; (kind<10) && (errors<10); kind++) { + isdir = kind & 1; + if (kind & 8) + owner = (const SID*)group_sid; + else + owner = (kind & 2 ? adminsid : (const SID*)owner_sid); + group = (kind & 4 ? adminsid : (const SID*)group_sid); + basictest(kind, isdir, owner, group); + } +#if POSIXACLS + for (k=0; (k<sizeof(kindlist)) && (errors<10); k++) { + kind = kindlist[k]; + isdir = kind & 1; + if (kind & 8) + owner = (const SID*)group_sid; + else + owner = (kind & 2 ? adminsid : (const SID*)owner_sid); + group = (kind & 4 ? adminsid : (const SID*)group_sid); + posixtest(kind, isdir, owner, group); + } + ntfs_free_mapping(context.mapping); +#endif + if (errors >= 10) + printf("** too many errors, test aborted\n"); +} +#endif /* SELFTESTS & !USESTUBS */ + +#ifdef WIN32 + +/* + * Get the security descriptor of a file (Windows version) + */ + +unsigned int getfull(char *attr, const char *fullname) +{ + static char part[MAXATTRSZ]; + BIGSID ownsid; + int xowner; + int ownersz; + u16 ownerfl; + ULONG attrsz; + ULONG partsz; + BOOL overflow; + HANDLE htoken; + TOKEN_PRIVILEGES tkp; + BOOL saclsuccess; + + attrsz = 0; + partsz = 0; + overflow = FALSE; + if (GetFileSecurityW((LPCWSTR)fullname,OWNER_SECURITY_INFORMATION, + (char*)part,MAXATTRSZ,&partsz)) { + xowner = get4l(part,4); + if (xowner) { + ownerfl = get2l(part,2); + ownersz = ntfs_sid_size((SID*)&part[xowner]); + if (ownersz <= (int)sizeof(BIGSID)) + memcpy(ownsid,&part[xowner],ownersz); + else + overflow = TRUE; + } else { + ownerfl = 0; + ownersz = 0; + } + /* + * SACL : just feed in or clean + * This requires the SE_SECURITY_NAME privilege + */ + saclsuccess = FALSE; + if (OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &htoken)) { + if (LookupPrivilegeValue(NULL, SE_SECURITY_NAME, + &tkp.Privileges[0].Luid)) { + tkp.PrivilegeCount = 1; + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0)) { + if (GetFileSecurityW((LPCWSTR)fullname, + SACL_SECURITY_INFORMATION, + (char*)attr,MAXATTRSZ,&attrsz)) { + saclsuccess = TRUE; + } + tkp.Privileges[0].Attributes = 0; + AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0); + } + } + } + if (!saclsuccess) { + attrsz = 20; + set4l(attr,0); + attr[0] = SECURITY_DESCRIPTOR_REVISION; + set4l(&attr[12],0); + if (opt_v >= 2) + printf(" No SACL\n"); + } + /* + * append DACL and merge its flags + */ + partsz = 0; + set4l(&attr[16],0); + if (GetFileSecurityW((LPCWSTR)fullname,DACL_SECURITY_INFORMATION, + (char*)part,MAXATTRSZ,&partsz)) { + if ((attrsz + partsz - 20) <= MAXATTRSZ) { + memcpy(&attr[attrsz],&part[20],partsz-20); + set4l(&attr[16],(partsz > 20 ? attrsz : 0)); + set2l(&attr[2],get2l(attr,2) | (get2l(part,2) + & const_le16_to_cpu(SE_DACL_PROTECTED + | SE_DACL_AUTO_INHERITED + | SE_DACL_PRESENT))); + attrsz += partsz - 20; + } else + overflow = TRUE; + } else + if (partsz > MAXATTRSZ) + overflow = TRUE; + else { + if (opt_b) + printf("# No discretionary access control list\n"); + else + printf(" No discretionary access control list\n"); + warnings++; + } + + /* + * append owner and merge its flag + */ + if (xowner && !overflow) { + memcpy(&attr[attrsz],ownsid,ownersz); + set4l(&attr[4],attrsz); + set2l(&attr[2],get2l(attr,2) + | (ownerfl & const_le16_to_cpu(SE_OWNER_DEFAULTED))); + attrsz += ownersz; + } else + set4l(&attr[4],0); + /* + * append group + */ + partsz = 0; + set4l(&attr[8],0); + if (GetFileSecurityW((LPCWSTR)fullname,GROUP_SECURITY_INFORMATION, + (char*)part,MAXATTRSZ,&partsz)) { + if ((attrsz + partsz - 20) <= MAXATTRSZ) { + memcpy(&attr[attrsz],&part[20],partsz-20); + set4l(&attr[8],(partsz > 20 ? attrsz : 0)); + set2l(&attr[2],get2l(attr,2) | (get2l(part,2) + & const_le16_to_cpu(SE_GROUP_DEFAULTED))); + attrsz += partsz - 20; + } else + overflow = TRUE; + } else + if (partsz > MAXATTRSZ) + overflow = TRUE; + else { + printf("** No group SID\n"); + warnings++; + } + set2l(&attr[2],get2l(attr,2) + | const_le16_to_cpu(SE_SELF_RELATIVE)); + if (overflow) { + printf("** Descriptor was too long (> %d)\n",MAXATTRSZ); + warnings++; + attrsz = 0; + } else + if (!ntfs_valid_descr((char*)attr,attrsz)) { + printf("** Descriptor for "); + printname(stdout,fullname); + printf(" is not valid\n"); + errors++; + attrsz = 0; + } + + } else { + printf("** Could not get owner of "); + printname(stdout,fullname); + printf(", partsz %d\n",partsz); + printerror(stdout); + warnings++; + attrsz = 0; + } + return (attrsz); +} + +/* + * Update a security descriptor (Windows version) + */ + +BOOL updatefull(const char *name, DWORD flags, char *attr) +{ + BOOL bad; + + bad = !SetFileSecurityW((LPCWSTR)name, flags, attr); + if (bad + && (flags & OWNER_SECURITY_INFORMATION) + && (GetLastError() == 1307)) { + printf("** Could not set owner of "); + printname(stdout,name); + printf(", retrying with no owner setting\n"); + warnings++; + bad = !SetFileSecurityW((LPCWSTR)name, + flags & ~OWNER_SECURITY_INFORMATION, (char*)attr); + } + if (bad) { + printf("** Could not change attributes of "); + printname(stdout,name); + printf("\n"); + printerror(stdout); + errors++; + } + return (!bad); +} + +#else + +/* + * Get the security descriptor of a file (Linux version) + */ + +unsigned int getfull(char *attr, const char *fullname) +{ + static char part[MAXATTRSZ]; + BIGSID ownsid; + int xowner; + int ownersz; + u16 ownerfl; + u32 attrsz; + u32 partsz; + BOOL overflow; + + attrsz = 0; + partsz = 0; + overflow = FALSE; + if (ntfs_get_file_security(ntfs_context,fullname, + OWNER_SECURITY_INFORMATION, + (char*)part,MAXATTRSZ,&partsz)) { + xowner = get4l(part,4); + if (xowner) { + ownerfl = get2l(part,2); + ownersz = ntfs_sid_size((SID*)&part[xowner]); + if (ownersz <= (int)sizeof(BIGSID)) + memcpy(ownsid,&part[xowner],ownersz); + else + overflow = TRUE; + } else { + ownerfl = 0; + ownersz = 0; + } + /* + * SACL : just feed in or clean + */ + if (!ntfs_get_file_security(ntfs_context,fullname, + SACL_SECURITY_INFORMATION, + (char*)attr,MAXATTRSZ,&attrsz)) { + attrsz = 20; + set4l(attr,0); + attr[0] = SECURITY_DESCRIPTOR_REVISION; + set4l(&attr[12],0); + if (opt_v >= 2) + printf(" No SACL\n"); + } + /* + * append DACL and merge its flags + */ + partsz = 0; + set4l(&attr[16],0); + if (ntfs_get_file_security(ntfs_context,fullname, + DACL_SECURITY_INFORMATION, + (char*)part,MAXATTRSZ,&partsz)) { + if ((attrsz + partsz - 20) <= MAXATTRSZ) { + memcpy(&attr[attrsz],&part[20],partsz-20); + set4l(&attr[16],(partsz > 20 ? attrsz : 0)); + set2l(&attr[2],get2l(attr,2) | (get2l(part,2) + & const_le16_to_cpu(SE_DACL_PROTECTED + | SE_DACL_AUTO_INHERITED + | SE_DACL_PRESENT))); + attrsz += partsz - 20; + } else + overflow = TRUE; + } else + if (partsz > MAXATTRSZ) + overflow = TRUE; + else { + if (opt_b) + printf("# No discretionary access control list\n"); + else + printf(" No discretionary access control list\n"); + warnings++; + } + + /* + * append owner and merge its flag + */ + if (xowner && !overflow) { + memcpy(&attr[attrsz],ownsid,ownersz); + set4l(&attr[4],attrsz); + set2l(&attr[2],get2l(attr,2) + | (ownerfl & const_le16_to_cpu(SE_OWNER_DEFAULTED))); + attrsz += ownersz; + } else + set4l(&attr[4],0); + /* + * append group + */ + partsz = 0; + set4l(&attr[8],0); + if (ntfs_get_file_security(ntfs_context,fullname, + GROUP_SECURITY_INFORMATION, + (char*)part,MAXATTRSZ,&partsz)) { + if ((attrsz + partsz - 20) <= MAXATTRSZ) { + memcpy(&attr[attrsz],&part[20],partsz-20); + set4l(&attr[8],(partsz > 20 ? attrsz : 0)); + set2l(&attr[2],get2l(attr,2) | (get2l(part,2) + & const_le16_to_cpu(SE_GROUP_DEFAULTED))); + attrsz += partsz - 20; + } else + overflow = TRUE; + } else + if (partsz > MAXATTRSZ) + overflow = TRUE; + else { + printf("** No group SID\n"); + warnings++; + } + if (overflow) { + printf("** Descriptor was too long (> %d)\n",MAXATTRSZ); + warnings++; + attrsz = 0; + } else + if (!ntfs_valid_descr((char*)attr,attrsz)) { + printf("** Descriptor for %s is not valid\n",fullname); + errors++; + attrsz = 0; + } + + } else { + printf("** Could not get owner of %s\n",fullname); + warnings++; + attrsz = 0; + } + return (attrsz); +} + +/* + * Update a security descriptor (Linux version) + */ + +BOOL updatefull(const char *name, DWORD flags, char *attr) +{ + BOOL ok; + + ok = !ntfs_set_file_security(ntfs_context, name, flags, attr); + if (ok) { + printf("** Could not change attributes of %s\n",name); + printerror(stdout); + errors++; + } + return (ok); +} + + +#endif + +#if POSIXACLS + +/* + * Set all the parameters associated to a file + */ + +BOOL setfull_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc, + BOOL isdir) +{ + static char attr[MAXATTRSZ]; + struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + char *newattr; + int err; + unsigned int attrsz; + int newattrsz; + const SID *usid; + const SID *gsid; +#if OWNERFROMACL + const SID *osid; +#endif + + printf("%s ",(isdir ? "Directory" : "File")); + printname(stdout,fullname); + if (pxdesc->acccnt) + printf("\n"); + else + printf(" mode 0%03o\n",pxdesc->mode); + + err = FALSE; + attrsz = getfull(attr, fullname); + if (attrsz) { + oldpxdesc = linux_permissions_posix(attr, isdir); + if (opt_v >= 2) { + printf("Posix equivalent of old ACL :\n"); + showposix(oldpxdesc); + } + if (oldpxdesc) { + if (!pxdesc->defcnt + && !(pxdesc->tagsset & + (POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK))) { + if (!ntfs_merge_mode_posix(oldpxdesc,pxdesc->mode)) + newpxdesc = oldpxdesc; + else { + newpxdesc = (struct POSIX_SECURITY*)NULL; + free(oldpxdesc); + } + } else { + newpxdesc = ntfs_merge_descr_posix(pxdesc, oldpxdesc); + free(oldpxdesc); + } + if (opt_v) { + printf("New Posix ACL :\n"); + showposix(newpxdesc); + } + } else + newpxdesc = (struct POSIX_SECURITY*)NULL; + if (newpxdesc) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + gsid = (const SID*)&attr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + osid = (const SID*)&attr[le32_to_cpu(phead->owner)]; + usid = ntfs_acl_owner((const char*)attr); + if (!ntfs_same_sid(usid,osid)) + printf("== Windows owner might change\n"); +#else + usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; +#endif + newattr = ntfs_build_descr_posix(context.mapping, + newpxdesc,isdir,usid,gsid); + free(newpxdesc); + } else + newattr = (char*)NULL; + if (newattr) { + newattrsz = ntfs_attr_size(newattr); + if (opt_v) { + printf("New NTFS security descriptor\n"); + hexdump(newattr,newattrsz,4); + } + if (opt_v >= 2) { + printf("Expected hash : 0x%08lx\n", + (unsigned long)hash((le32*)newattr,ntfs_attr_size(newattr))); + showheader(newattr,0); + showusid(newattr,0); + showgsid(newattr,0); + showdacl(newattr,isdir,0); + showsacl(newattr,isdir,0); + } + +#ifdef WIN32 + /* + * avoid getting a set owner error on Windows + * owner should not be changed anyway + */ + if (!updatefull(fullname, + DACL_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | OWNER_SECURITY_INFORMATION, + newattr)) +#else + if (!updatefull(fullname, + DACL_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | OWNER_SECURITY_INFORMATION, + newattr)) +#endif + err = TRUE; +/* +{ +struct POSIX_SECURITY *interp; +printf("Reinterpreted new Posix :\n"); +interp = linux_permissions_posix(newattr,isdir); +showposix(interp); +free(interp); +} +*/ + free(newattr); + } else + err = TRUE; + } else + err = TRUE; + return (!err); +} + +#endif + +BOOL setfull(const char *fullname, int mode, BOOL isdir) +{ + static char attr[MAXATTRSZ]; + const SECURITY_DESCRIPTOR_RELATIVE *phead; + char *newattr; + int err; + unsigned int attrsz; + int newattrsz; + const SID *usid; + const SID *gsid; +#if OWNERFROMACL + const SID *osid; +#endif + + printf("%s ",(isdir ? "Directory" : "File")); + printname(stdout,fullname); + printf(" mode 0%03o\n",mode); + attrsz = getfull(attr, fullname); + err = FALSE; + if (attrsz) { + phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; + gsid = (const SID*)&attr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + osid = (const SID*)&attr[le32_to_cpu(phead->owner)]; + usid = ntfs_acl_owner((const char*)attr); + if (!ntfs_same_sid(usid,osid)) + printf("== Windows owner might change\n"); +#else + usid = (const SID*)&attr[le32_to_cpu(phead->owner)]; +#endif + newattr = ntfs_build_descr(mode,isdir,usid,gsid); + if (newattr) { + newattrsz = ntfs_attr_size(newattr); + if (opt_v) { + printf("Security descriptor\n"); + hexdump(newattr,newattrsz,4); + } + if (opt_v >= 2) { + printf("Expected hash : 0x%08lx\n", + (unsigned long)hash((le32*)newattr,ntfs_attr_size(newattr))); + showheader(newattr,0); + showusid(newattr,0); + showgsid(newattr,0); + showdacl(newattr,isdir,0); + showsacl(newattr,isdir,0); + } + +#ifdef WIN32 + /* + * avoid getting a set owner error on Windows + * owner should not be changed anyway + */ + if (!updatefull(fullname, + DACL_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | OWNER_SECURITY_INFORMATION, + newattr)) +#else + if (!updatefull(fullname, + DACL_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION + | OWNER_SECURITY_INFORMATION, + newattr)) +#endif + err = TRUE; + free(newattr); + } + + } else + err = TRUE; + return (err); +} + +BOOL proposal(const char *name, const char *attr) +{ + char fullname[MAXFILENAME]; + int uoff, goff; + int i; + u64 uauth, gauth; + int ucnt, gcnt; + int uid, gid; + BOOL err; +#ifdef WIN32 + char driveletter; +#else + struct stat st; + char *p,*q; +#endif + + err = FALSE; +#ifdef WIN32 + uid = gid = 0; +#else + uid = getuid(); + gid = getgid(); +#endif + uoff = get4l(attr,4); + uauth = get6h(attr,uoff+2); + ucnt = attr[uoff+1] & 255; + goff = get4l(attr,8); + gauth = get6h(attr,goff+2); + gcnt = attr[goff+1] & 255; + + if ((ucnt == 5) && (gcnt == 5) + && (uauth == 5) && (gauth == 5) + && (get4l(attr,uoff+8) == 21) && (get4l(attr,goff+8) == 21)) { + printf("# User mapping proposal :\n"); + printf("# -------------------- cut here -------------------\n"); + if (uid) + printf("%d::",uid); + else + printf("user::"); + printf("S-%d-%llu",attr[uoff] & 255,uauth); + for (i=0; i<ucnt; i++) + printf("-%lu",get4l(attr,uoff+8+4*i)); + printf("\n"); + if (gid) + printf(":%d:",gid); + else + printf(":group:"); + printf("S-%d-%llu",attr[goff] & 255,gauth); + for (i=0; i<gcnt; i++) + printf("-%lu",get4l(attr,goff+8+4*i)); + printf("\n"); + /* generic rule, based on group */ + printf("::S-%d-%llu",attr[goff] & 255,gauth); + for (i=0; i<gcnt-1; i++) + printf("-%lu",get4l(attr,goff+8+4*i)); + printf("-10000\n"); + printf("# -------------------- cut here -------------------\n"); + if (!uid || !gid) { + printf("# Please replace \"user\" and \"group\" above by the uid\n"); + printf("# and gid of the Linux owner and group of "); + printname(stdout,name); + printf(", then\n"); + printf("# insert the modified lines into .NTFS-3G/Usermapping, with .NTFS-3G\n"); + } else + printf("# Insert the above lines into .NTFS-3G/Usermapping, with .NTFS-3G\n"); +#ifdef WIN32 + printf("# being a directory of the root of the NTFS file system.\n"); + + /* Get the drive letter to the file system */ + driveletter = 0; + if ((((name[0] >= 'a') && (name[0] <= 'z')) + || ((name[0] >= 'A') && (name[0] <= 'Z'))) + && (name[1] == ':')) + driveletter = name[0]; + else { + if (GetCurrentDirectoryA(MAXFILENAME, fullname) + && (fullname[1] == ':')) + driveletter = fullname[0]; + } + if (driveletter) { + printf("# Example : %c:\\.NTFS-3G\\UserMapping\n", + driveletter); + } +#else + printf("# being a hidden subdirectory of the root of the NTFS file system.\n"); + + /* Get the path to the root of the file system */ + if (name[0] != '/') { + p = getcwd(fullname,MAXFILENAME); + if (p) { + strcat(fullname,"/"); + strcat(fullname,name); + } + } else { + strcpy(fullname,name); + p = fullname; + } + if (p) { + /* go down the path to inode 5 (fails on symlinks) */ + do { + lstat(fullname,&st); + q = strrchr(p,'/'); + if (q && (st.st_ino != 5)) + *q = 0; + } while (strchr(p,'/') && (st.st_ino != 5)); + } + if (p && (st.st_ino == 5)) { + printf("# Example : "); + printname(stdout,p); + printf("/.NTFS-3G/UserMapping\n"); + } +#endif + } else { + printf("** Not possible : "); + printname(stdout,name); + printf(" was not created by a Windows user\n"); + err = TRUE; + } + return (err); +} + +#ifdef WIN32 + +/* + * Check whether a directory with reparse data is a junction + * or a symbolic link + */ + +BOOL islink(const char *filename) +{ + WIN32_FIND_DATAW found; + HANDLE search; + BOOL link; + + link = FALSE; + search = FindFirstFileW((LPCWSTR)filename, &found); + if (search != INVALID_HANDLE_VALUE) { + link = (found.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) + || (found.dwReserved0 == IO_REPARSE_TAG_SYMLINK); + FindClose(search); + } + return (link); +} + +#if POSIXACLS +BOOL iterate(RECURSE call, const char *fullname, const struct POSIX_SECURITY *pxdesc) +#else +BOOL iterate(RECURSE call, const char *fullname, mode_t mode) +#endif +{ + WIN32_FIND_DATAW found; + HANDLE search; + BOOL err; + unsigned int len; + char *filter; + char *inner; + + err = FALSE; + filter = (char*)malloc(MAXFILENAME); + inner = (char*)malloc(MAXFILENAME); + if (filter && inner) { + len = utf16len(fullname); + memcpy(filter, fullname, 2*len); + makeutf16(&filter[2*len],"\\*.*"); + search = FindFirstFileW((LPCWSTR)filter, &found); + if (search != INVALID_HANDLE_VALUE) { + do { + if (found.cFileName[0] != UNICODE('.')) { + memcpy(inner, fullname, 2*len); + inner[2*len] = '\\'; + inner[2*len+1] = 0; + memcpy(&inner[2*len+2],found.cFileName, + 2*utf16len((char*)found.cFileName)+2); + if (opt_v) + if (opt_b) + printf("#\n#\n"); + else + printf("\n\n"); + switch (call) { + case RECSHOW : + if (recurseshow(inner)) + err = TRUE; + break; +#if POSIXACLS + case RECSETPOSIX : + if (recurseset_posix(inner,pxdesc)) + err = TRUE; + break; +#else + case RECSET : + if (recurseset(inner,mode)) + err = TRUE; + break; +#endif + default : + err = TRUE; + } + } + } while (FindNextFileW(search, &found)); + FindClose(search); + } + free(filter); + free(inner); + } else { + printf("** Cannot process deeper : not enough memory\n"); + errors++; + err = TRUE; + } + return (err); +} + + + +/* + * Display all the parameters associated to a file (Windows version) + */ + +void showfull(const char *fullname, BOOL isdir) +{ + static char attr[MAXATTRSZ]; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + ULONG attrsz; + int mode; + uid_t uid; + gid_t gid; + int attrib; + int level; + + printf("%s ",(isdir ? "Directory" : "File")); + printname(stdout,fullname); + printf("\n"); + + /* get individual parameters, as when trying to get them */ + /* all, and one (typically SACL) is missing, we get none, */ + /* and concatenate them, to be able to compute the hash code */ + + attrsz = getfull(attr, fullname); + if (attrsz) { + if (opt_v || opt_b) { + hexdump(attr,attrsz,8); + printf("Computed hash : 0x%08lx\n", + (unsigned long)hash((le32*)attr,attrsz)); + } + if (opt_v && opt_b) { + printf("# %s ",(isdir ? "Directory" : "File")); + printname(stdout,fullname); + printf(" hash 0x%lx\n", + (unsigned long)hash((le32*)attr,attrsz)); + } + attrib = GetFileAttributesW((LPCWSTR)fullname); + if (attrib == INVALID_FILE_ATTRIBUTES) { + printf("** Could not get file attrib\n"); + errors++; + } else + printf("Windows attrib : 0x%x\n",attrib); + if (ntfs_valid_descr(attr,attrsz)) { +#if POSIXACLS + pxdesc = linux_permissions_posix(attr,isdir); + if (pxdesc) + mode = pxdesc->mode; + else + mode = 0; +#else + mode = linux_permissions(attr,isdir); +#endif + if (opt_v >= 2) { + level = (opt_b ? 4 : 0); + showheader(attr,level); + showusid(attr,level); + showgsid(attr,level); + showdacl(attr,isdir,level); + showsacl(attr,isdir,level); + } + uid = linux_owner(attr); + gid = linux_group(attr); + if (opt_b) { + showownership(attr); + printf("# Interpreted Unix owner %d, group %d, mode 0%03o\n", + (int)uid,(int)gid,mode); + } else { + showownership(attr); + printf("Interpreted Unix owner %d, group %d, mode 0%03o\n", + (int)uid,(int)gid,mode); + } +#if POSIXACLS + if (pxdesc) { + if (!opt_b + && (pxdesc->defcnt + || (pxdesc->tagsset + & (POSIX_ACL_USER + | POSIX_ACL_GROUP + | POSIX_ACL_MASK)))) + showposix(pxdesc); + free(pxdesc); + } +#endif + } else + if (opt_b) + printf("# Descriptor fails sanity check\n"); + else + printf("Descriptor fails sanity check\n"); + } +} + +BOOL recurseshow(const char *fullname) +{ + int attrib; + int err; + BOOL isdir; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)fullname); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + showfull(fullname,isdir); + if (isdir + && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT) + && islink(fullname))) { +#if POSIXACLS + err = iterate(RECSHOW, fullname, (struct POSIX_SECURITY*)NULL); +#else + err = iterate(RECSHOW, fullname, 0); +#endif + } + } else { + printf("** Could not access "); + printname(stdout,fullname); + printf("\n"); + printerror(stdout); + errors++; + err = TRUE; + } + return (err); +} + + +BOOL singleshow(const char *fullname) +{ + int attrib; + int err; + BOOL isdir; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)fullname); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + showfull(fullname,isdir); + } else { + printf("** Could not access "); + printname(stdout,fullname); + printf("\n"); + printerror(stdout); + errors++; + err = TRUE; + } + return (err); +} + +BOOL mapproposal(const char *fullname) +{ + char attr[256]; + ULONG attrsz; + int attrib; + int err; + + err = FALSE; + attrsz = 0; + attrib = GetFileAttributesW((LPCWSTR)fullname); + if ((attrib != INVALID_FILE_ATTRIBUTES) + && GetFileSecurityW((LPCWSTR)fullname, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, + (char*)attr,256,&attrsz)) { + err = proposal(fullname,attr); + } else { + printf("** Could not access "); + printname(stdout,fullname); + printf("\n"); + printerror(stdout); + err = TRUE; + } + return (err); +} + +#if POSIXACLS + +BOOL recurseset_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc) +{ + int attrib; + int err; + BOOL isdir; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)fullname); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + err = !setfull_posix(fullname,pxdesc,isdir); + if (err) { + printf("** Failed to update "); + printname(stdout,fullname); + printf("\n"); + errors++; + } else + if (isdir + && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT) + && islink(fullname))) + iterate(RECSETPOSIX, fullname, pxdesc); + } else { + err = GetLastError(); + printf("** Could not access "); + printname(stdout,fullname); + printf("\n"); + printerror(stdout); + err = TRUE; + errors++; + } + return (err); +} + +#else + +BOOL recurseset(const char *fullname, int mode) +{ + int attrib; + int err; + BOOL isdir; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)fullname); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + setfull(fullname,mode,isdir); + if (isdir + && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT) + && islink(fullname))) + iterate(RECSETPOSIX, fullname, mode); + } else { + err = GetLastError(); + printf("** Could not access "); + printname(stdout,fullname); + printf("\n"); + printerror(stdout); + err = TRUE; + errors++; + } + return (err); +} + +#endif + +#if POSIXACLS + +BOOL singleset_posix(const char *path, const struct POSIX_SECURITY *pxdesc) +{ + BOOL isdir; + BOOL err; + int attrib; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)path); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY); + err = !setfull_posix(path,pxdesc,isdir); + if (err) { + printf("** Failed to update "); + printname(stdout,path); + printf("\n"); + errors++; + } + } else { + printf("** Could not access "); + printname(stdout,path); + printf("\n"); + printerror(stdout); + errors++; + err = TRUE; + } + return (!err); +} + +#endif + +BOOL singleset(const char *path, int mode) +{ + BOOL isdir; + BOOL err; + int attrib; + + err = FALSE; + attrib = GetFileAttributesW((LPCWSTR)path); + if (attrib != INVALID_FILE_ATTRIBUTES) { + isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY); + setfull(path,mode,isdir); + } else { + printf("** Could not access "); + printname(stdout,path); + printf("\n"); + printerror(stdout); + errors++; + err = TRUE; + } + return (!err); +} + +#else + +/* + * Display all the parameters associated to a file (Linux version) + */ + +void showfull(const char *fullname, BOOL isdir) +{ + static char attr[MAXATTRSZ]; + static char part[MAXATTRSZ]; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + struct SECURITY_DATA *psecurdata; + char *newattr; + int securindex; + int mode; + int level; + int attrib; + u32 attrsz; + u32 partsz; + uid_t uid; + gid_t gid; + + if (opt_v || opt_b) + printf("%s %s\n",(isdir ? "Directory" : "File"),fullname); + + /* get individual parameters, as when trying to get them */ + /* all, and one (typically SACL) is missing, we get none */ + /* and concatenate them, to be able to compute the checksum */ + + partsz = 0; + securindex = ntfs_get_file_security(ntfs_context,fullname, + OWNER_SECURITY_INFORMATION, + (char*)part,MAXATTRSZ,&partsz); + + attrib = ntfs_get_file_attributes(ntfs_context, fullname); + if (attrib == INVALID_FILE_ATTRIBUTES) { + printf("** Could not get file attrib\n"); + errors++; + } + if ((securindex < 0) + || (securindex >= MAXSECURID) + || ((securindex > 0) + && ((!opt_r && !opt_b) + || !securdata[securindex >> SECBLKSZ] + || !securdata[securindex >> SECBLKSZ][securindex & ((1 << SECBLKSZ) - 1)].filecount))) + { + if (opt_v || opt_b) { + if ((securindex < -1) || (securindex >= MAXSECURID)) + printf("Security key : 0x%x out of range\n",securindex); + else + if (securindex == -1) + printf("Security key : none\n"); + else + printf("Security key : 0x%x\n",securindex); + } else { + printf("%s %s",(isdir ? "Directory" : "File"),fullname); + if ((securindex < -1) || (securindex >= MAXSECURID)) + printf(" : key 0x%x out of range\n",securindex); + else + if (securindex == -1) + printf(" : no key\n"); + else + printf(" : key 0x%x\n",securindex); + } + + attrsz = getfull(attr, fullname); + if (attrsz) { + psecurdata = (struct SECURITY_DATA*)NULL; + if ((securindex < MAXSECURID) && (securindex > 0)) { + if (!securdata[securindex >> SECBLKSZ]) + newblock(securindex); + if (securdata[securindex >> SECBLKSZ]) + psecurdata = &securdata[securindex >> SECBLKSZ] + [securindex & ((1 << SECBLKSZ) - 1)]; + } + if (opt_v && (opt_a || opt_b) && psecurdata) { + newattr = (char*)malloc(attrsz); + printf("# %s %s hash 0x%lx\n",(isdir ? "Directory" : "File"), + fullname, + (unsigned long)hash((le32*)attr,attrsz)); + if (newattr) { + memcpy(newattr,attr,attrsz); + psecurdata->attr = newattr; + } + } + if ((opt_v || opt_b) + && ((securindex >= MAXSECURID) + || (securindex <= 0) + || !psecurdata + || (!psecurdata->filecount + && !psecurdata->flags))) { + hexdump(attr,attrsz,8); + printf("Computed hash : 0x%08lx\n", + (unsigned long)hash((le32*)attr,attrsz)); + } + if (ntfs_valid_descr((char*)attr,attrsz)) { +#if POSIXACLS + pxdesc = linux_permissions_posix(attr,isdir); + if (pxdesc) + mode = pxdesc->mode; + else + mode = 0; +#else + mode = linux_permissions(attr,isdir); +#endif + attrib = ntfs_get_file_attributes(ntfs_context,fullname); + if (opt_v >= 2) { + level = (opt_b ? 4 : 0); + showheader(attr,level); + showusid(attr,level); + showgsid(attr,level); + showdacl(attr,isdir,level); + showsacl(attr,isdir,level); + } + if (attrib != INVALID_FILE_ATTRIBUTES) + printf("Windows attrib : 0x%x\n",attrib); + uid = linux_owner(attr); + gid = linux_group(attr); + if (opt_b) { + showownership(attr); + printf("# Interpreted Unix owner %d, group %d, mode 0%03o\n", + (int)uid,(int)gid,mode); + } else { + showownership(attr); + printf("Interpreted Unix owner %d, group %d, mode 0%03o\n", + (int)uid,(int)gid,mode); + } +#if POSIXACLS + if (pxdesc) { + if (!opt_b + && (pxdesc->defcnt + || (pxdesc->tagsset + & (POSIX_ACL_USER + | POSIX_ACL_GROUP + | POSIX_ACL_MASK)))) + showposix(pxdesc); +#if USESTUBS + stdfree(pxdesc); /* allocated within library */ +#else + free(pxdesc); +#endif + } +#endif + if ((opt_r || opt_b) && (securindex < MAXSECURID) + && (securindex > 0) && psecurdata) { + psecurdata->filecount++; + psecurdata->mode = mode; + } + } else { + printf("** Descriptor fails sanity check\n"); + errors++; + } + } + } else + if (securindex > 0) { + if (securdata[securindex >> SECBLKSZ]) { + psecurdata = &securdata[securindex >> SECBLKSZ] + [securindex & ((1 << SECBLKSZ) - 1)]; + psecurdata->filecount++; + if (opt_b || opt_r) { + if (!opt_b && !opt_v) + printf("%s %s\n",(isdir ? "Directory" : "File"),fullname); + printf("Security key : 0x%x mode %03o (already displayed)\n", + securindex,psecurdata->mode); + if (attrib != INVALID_FILE_ATTRIBUTES) + printf("Windows attrib : 0x%x\n",attrib); + } else { + printf("%s %s",(isdir ? "Directory" : "File"),fullname); + printf(" : key 0x%x\n",securindex); + } + if ((opt_a || opt_b) && opt_v + && psecurdata && psecurdata->attr) { + printf("# %s %s hash 0x%lx\n",(isdir ? "Directory" : "File"), + fullname, + (unsigned long)hash((le32*)psecurdata->attr, + ntfs_attr_size(psecurdata->attr))); + } + } + } else { + if (!opt_v && !opt_b) + printf("%s %s",(isdir ? "Directory" : "File"),fullname); + printf(" (Failed)\n"); + printf("** Could not get security data of %s, partsz %d\n", + fullname,partsz); + printerror(stdout); + errors++; + } +} + +BOOL recurseshow(const char *path) +{ + struct CALLBACK dircontext; + struct LINK *current; + BOOL isdir; + BOOL err; + + err = FALSE; + dircontext.head = (struct LINK*)NULL; + dircontext.dir = path; + isdir = ntfs_read_directory(ntfs_context, path, + callback, &dircontext); + if (isdir) { + showfull(path,TRUE); + if (opt_v) { + if (opt_b) + printf("#\n#\n"); + else + printf("\n\n"); + } + while (dircontext.head) { + current = dircontext.head; + if (recurseshow(current->name)) err = TRUE; + dircontext.head = dircontext.head->next; + free(current); + } + } else + if (errno == ENOTDIR) { + showfull(path,FALSE); + if (opt_v) { + if (opt_b) + printf("#\n#\n"); + else + printf("\n\n"); + } + } else { + printf("** Could not access %s\n",path); + printerror(stdout); + errors++; + err = TRUE; + } + return (!err); +} + + +BOOL singleshow(const char *path) +{ + BOOL isdir; + BOOL err; + + err = FALSE; + isdir = ntfs_read_directory(ntfs_context, path, + callback, (struct CALLBACK*)NULL); + if (isdir || (errno == ENOTDIR)) + showfull(path,isdir); + else { + printf("** Could not access %s\n",path); + printerror(stdout); + errors++; + err = TRUE; + } + return (err); +} + +#ifdef HAVE_SETXATTR + +static ssize_t ntfs_getxattr(const char *path, const char *name, void *value, size_t size) +{ +#if defined(__APPLE__) || defined(__DARWIN__) + return getxattr(path, name, value, size, 0, 0); +#else + return getxattr(path, name, value, size); +#endif +} + +/* + * Display all the parameters associated to a mounted file + */ + +BOOL showmounted(const char *fullname) +{ + + static char attr[MAXATTRSZ]; + struct stat st; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + BOOL mapped; + int attrsz; + int mode; + uid_t uid; + gid_t gid; + u32 attrib; + int level; + BOOL isdir; + BOOL err; + + err = FALSE; + if (!stat(fullname,&st)) { + isdir = S_ISDIR(st.st_mode); + printf("%s ",(isdir ? "Directory" : "File")); + printname(stdout,fullname); + printf("\n"); + + attrsz = ntfs_getxattr(fullname,"system.ntfs_acl",attr,MAXATTRSZ); + if (attrsz > 0) { + if (opt_v) { + hexdump(attr,attrsz,8); + printf("Computed hash : 0x%08lx\n", + (unsigned long)hash((le32*)attr,attrsz)); + } + if (ntfs_getxattr(fullname,"system.ntfs_attrib",&attrib,4) != 4) { + printf("** Could not get file attrib\n"); + errors++; + } else + printf("Windows attrib : 0x%x\n",(int)attrib); + if (ntfs_valid_descr(attr,attrsz)) { + mapped = !local_build_mapping(context.mapping,fullname); +#if POSIXACLS + if (mapped) { + pxdesc = linux_permissions_posix(attr,isdir); + if (pxdesc) + mode = pxdesc->mode; + else + mode = 0; + } else { + pxdesc = (struct POSIX_SECURITY*)NULL; + mode = linux_permissions(attr,isdir); + printf("No user mapping : " + "cannot display the Posix ACL\n"); + } +#else + mode = linux_permissions(attr,isdir); +#endif + if (opt_v >= 2) { + level = (opt_b ? 4 : 0); + showheader(attr,level); + showusid(attr,level); + showgsid(attr,level); + showdacl(attr,isdir,level); + showsacl(attr,isdir,level); + } + showownership(attr); + if (mapped) { + uid = linux_owner(attr); + gid = linux_group(attr); + printf("Interpreted Unix owner %d, group %d, mode 0%03o\n", + (int)uid,(int)gid,mode); + } else { + printf("Interpreted Unix mode 0%03o (owner and group are unmapped)\n", + mode); + } +#if POSIXACLS + if (pxdesc) { + if ((pxdesc->defcnt + || (pxdesc->tagsset + & (POSIX_ACL_USER + | POSIX_ACL_GROUP + | POSIX_ACL_MASK)))) + showposix(pxdesc); +#if USESTUBS + stdfree(pxdesc); /* allocated within library */ +#else + free(pxdesc); +#endif + } + if (mapped) + ntfs_free_mapping(context.mapping); +#endif + } else { + printf("Descriptor fails sanity check\n"); + errors++; + } + } else { + printf("** Could not get the NTFS ACL, check whether file is on NTFS\n"); + errors++; + } + } else { + printf("%s not found\n",fullname); + err = TRUE; + } + return (err); +} + +BOOL processmounted(const char *fullname) +{ + + static char attr[MAXATTRSZ]; + struct stat st; + int attrsz; + BOOL err; + + err = FALSE; + if (!opt_u) + err = showmounted(fullname); + else + if (!stat(fullname,&st)) { + attrsz = ntfs_getxattr(fullname,"system.ntfs_acl",attr,MAXATTRSZ); + if (attrsz > 0) { + if (opt_v) { + hexdump(attr,attrsz,8); + printf("Computed hash : 0x%08lx\n", + (unsigned long)hash((le32*)attr,attrsz)); + } + if (ntfs_valid_descr(attr,attrsz)) { + err = proposal(fullname, attr); + } else { + printf("*** Descriptor fails sanity check\n"); + errors++; + } + } else { + printf("** Could not get the NTFS ACL, check whether file is on NTFS\n"); + errors++; + } + } else { + printf("%s not found\n",fullname); + err = TRUE; + } + return (err); +} + +#else /* HAVE_SETXATTR */ + +BOOL processmounted(const char *fullname __attribute__((unused))) +{ + fprintf(stderr,"Not possible on this configuration,\n"); + fprintf(stderr,"you have to use an unmounted partition\n"); + return (TRUE); +} + +#endif /* HAVE_SETXATTR */ + +#if POSIXACLS + +BOOL recurseset_posix(const char *path, const struct POSIX_SECURITY *pxdesc) +{ + struct CALLBACK dircontext; + struct LINK *current; + BOOL isdir; + BOOL err; + + err = FALSE; + dircontext.head = (struct LINK*)NULL; + dircontext.dir = path; + isdir = ntfs_read_directory(ntfs_context, path, + callback, &dircontext); + if (isdir) { + err = !setfull_posix(path,pxdesc,TRUE); + if (err) { + printf("** Failed to update %s\n",path); + printerror(stdout); + errors++; + } else { + if (opt_b) + printf("#\n#\n"); + else + printf("\n\n"); + while (dircontext.head) { + current = dircontext.head; + recurseset_posix(current->name,pxdesc); + dircontext.head = dircontext.head->next; + free(current); + } + } + } else + if (errno == ENOTDIR) { + err = !setfull_posix(path,pxdesc,FALSE); + if (err) { + printf("** Failed to update %s\n",path); + printerror(stdout); + errors++; + } + } else { + printf("** Could not access %s\n",path); + printerror(stdout); + errors++; + err = TRUE; + } + return (!err); +} + +#else + +BOOL recurseset(const char *path, int mode) +{ + struct CALLBACK dircontext; + struct LINK *current; + BOOL isdir; + BOOL err; + + err = FALSE; + dircontext.head = (struct LINK*)NULL; + dircontext.dir = path; + isdir = ntfs_read_directory(ntfs_context, path, + callback, &dircontext); + if (isdir) { + setfull(path,mode,TRUE); + if (opt_b) + printf("#\n#\n"); + else + printf("\n\n"); + while (dircontext.head) { + current = dircontext.head; + recurseset(current->name,mode); + dircontext.head = dircontext.head->next; + free(current); + } + } else + if (errno == ENOTDIR) + setfull(path,mode,FALSE); + else { + printf("** Could not access %s\n",path); + printerror(stdout); + errors++; + err = TRUE; + } + return (!err); +} + +#endif + +#if POSIXACLS + +BOOL singleset_posix(const char *path, const struct POSIX_SECURITY *pxdesc) +{ + BOOL isdir; + BOOL err; + + err = FALSE; + isdir = ntfs_read_directory(ntfs_context, path, + callback, (struct CALLBACK*)NULL); + if (isdir || (errno == ENOTDIR)) { + err = !setfull_posix(path,pxdesc,isdir); + if (err) { + printf("** Failed to update %s\n",path); + printerror(stdout); + errors++; + } + } else { + printf("** Could not access %s\n",path); + printerror(stdout); + errors++; + err = TRUE; + } + return (!err); +} + +#endif + +BOOL singleset(const char *path, int mode) +{ + BOOL isdir; + BOOL err; + + err = FALSE; + isdir = ntfs_read_directory(ntfs_context, path, + callback, (struct CALLBACK*)NULL); + if (isdir || (errno == ENOTDIR)) + setfull(path,mode,isdir); + else { + printf("** Could not access %s\n",path); + printerror(stdout); + errors++; + err = TRUE; + } + return (!err); +} + +int callback(struct CALLBACK *dircontext, char *ntfsname, + int length, int type, + long long pos __attribute__((unused)), u64 mft_ref __attribute__((unused)), + unsigned int dt_type __attribute__((unused))) +{ + struct LINK *linkage; + char *name; + int newlth; + int size; + + size = utf8size(ntfsname,length); + if (dircontext + && (type != 2) /* 2 : dos name (8+3) */ + && (size > 0) /* chars convertible to utf8 */ + && ((length > 2) + || (ntfsname[0] != '.') + || (ntfsname[1] != '\0') + || ((ntfsname[2] || ntfsname[3]) + && ((ntfsname[2] != '.') || (ntfsname[3] != '\0'))))) { + linkage = (struct LINK*)malloc(sizeof(struct LINK) + + strlen(dircontext->dir) + + size + 2); + if (linkage) { + /* may find ".fuse_hidden*" files */ + /* recommendation is not to hide them, so that */ + /* the user has a clue to delete them */ + strcpy(linkage->name,dircontext->dir); + if (linkage->name[strlen(linkage->name) - 1] != '/') + strcat(linkage->name,"/"); + name = &linkage->name[strlen(linkage->name)]; + newlth = makeutf8(name,ntfsname,length); + name[newlth] = 0; + linkage->next = dircontext->head; + dircontext->head = linkage; + } + } + return (0); +} + +#endif + +#ifdef WIN32 + +/* + * Backup security descriptors in a directory tree (Windows mode) + */ + +BOOL backup(const char *root) +{ + BOOL err; + time_t now; + const char *txtime; + + now = time((time_t*)NULL); + txtime = ctime(&now); + printf("#\n# Recursive ACL collection on %s#\n",txtime); + err = recurseshow(root); + return (err); +} + +#else + +/* + * Backup security descriptors in a directory tree (Linux mode) + */ + +BOOL backup(const char *volume, const char *root) +{ + BOOL err; + int count; + int i,j; + time_t now; + const char *txtime; + + now = time((time_t*)NULL); + txtime = ctime(&now); + if (!getuid() && open_security_api()) { + if (open_volume(volume,NTFS_MNT_RDONLY)) { + printf("#\n# Recursive ACL collection on %s#\n",txtime); + err = recurseshow(root); + count = 0; + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + if (securdata[i]) + for (j=0; j<(1 << SECBLKSZ); j++) + if (securdata[i][j].filecount) { + count++; + } + printf("# %d security keys\n",count); + close_volume(volume); + } else { + fprintf(stderr,"Could not open volume %s\n",volume); + printerror(stdout); + err = TRUE; + } + close_security_api(); + } else { + if (getuid()) + fprintf(stderr,"This is only possible as root\n"); + else + fprintf(stderr,"Could not open security API\n"); + err = TRUE; + } + return (err); +} + +#endif + +#ifdef WIN32 + +/* + * List security descriptors in a directory tree (Windows mode) + */ + +BOOL listfiles(const char *root) +{ + BOOL err; + + if (opt_r) { + printf("\nRecursive file check\n"); + err = recurseshow(root); + } else + err = singleshow(root); + return (err); +} + +#else + +/* + * List security descriptors in a directory tree (Linux mode) + */ + +BOOL listfiles(const char *volume, const char *root) +{ + BOOL err; + int i,j; + int count; + + if (!getuid() && open_security_api()) { + if (open_volume(volume,NTFS_MNT_RDONLY)) { + if (opt_r) { + printf("\nRecursive file check\n"); + err = recurseshow(root); + printf("Summary\n"); + count = 0; + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + if (securdata[i]) + for (j=0; j<(1 << SECBLKSZ); j++) + if (securdata[i][j].filecount) { + printf("Key 0x%x : %d files, mode 0%03o\n", + i*(1 << SECBLKSZ)+j,securdata[i][j].filecount, + securdata[i][j].mode); + count++; + } + printf("%d security keys\n",count); + } else + err = singleshow(root); + close_volume(volume); + } else { + fprintf(stderr,"Could not open volume %s\n",volume); + printerror(stdout); + err = TRUE; + } + close_security_api(); + } else { + if (getuid()) + fprintf(stderr,"This is only possible as root\n"); + else + fprintf(stderr,"Could not open security API\n"); + err = TRUE; + } + return (err); +} + +BOOL mapproposal(const char *volume, const char *name) +{ + BOOL err; + u32 attrsz; + int securindex; + char attr[256]; /* header (20) and a couple of SIDs (max 40 each) */ + + err = FALSE; + if (!getuid() && open_security_api()) { + if (open_volume(volume,NTFS_MNT_RDONLY)) { + + attrsz = 0; + securindex = ntfs_get_file_security(ntfs_context,name, + OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION, + (char*)attr,MAXATTRSZ,&attrsz); + if (securindex) + err = proposal(name,attr); + else { + fprintf(stderr,"*** Could not get the ACL of %s\n", + name); + printerror(stdout); + errors++; + } + close_volume(volume); + } else { + fprintf(stderr,"Could not open volume %s\n",volume); + printerror(stdout); + err = TRUE; + } + close_security_api(); + } else { + if (getuid()) + fprintf(stderr,"This is only possible as root\n"); + else + fprintf(stderr,"Could not open security API\n"); + err = TRUE; + } + return (err); +} + +#endif + +#ifndef WIN32 + +/* + * Check whether a SDS entry is valid + */ + +BOOL valid_sds(const char *attr, unsigned int offset, + unsigned int entrysz, unsigned int size, u32 prevkey, + BOOL second) +{ + BOOL unsane; + u32 comphash; + u32 key; + + unsane = FALSE; + if (!get4l(attr,0) && !get4l(attr,4)) { + printf("Entry at 0x%lx was deleted\n",(long)offset); + } else { + if ((ntfs_attr_size(&attr[20]) + 20) > entrysz) { + printf("** Entry is truncated (expected size %ld)\n", + (long)ntfs_attr_size(&attr[20] + 20)); + unsane = TRUE; + errors++; + } + if ((ntfs_attr_size(&attr[20]) + 20) < entrysz) { + printf("** Extra data appended to entry (expected size %ld)\n", + (long)ntfs_attr_size(&attr[20]) + 20); + warnings++; + } + if (!unsane && !ntfs_valid_descr((const char*)&attr[20],size)) { + printf("** General sanity check has failed\n"); + unsane = TRUE; + errors++; + } + if (!unsane) { + comphash = hash((const le32*)&attr[20],entrysz-20); + if ((u32)get4l(attr,0) == comphash) { + if (opt_v >= 2) + printf("Hash 0x%08lx (correct)\n", + (unsigned long)comphash); + } else { + printf("** hash 0x%08lx (computed : 0x%08lx)\n", + (unsigned long)get4l(attr,0), + (unsigned long)comphash); + unsane = TRUE; + errors++; + } + } + if (!unsane) { + if ((second ? get8l(attr,8) + 0x40000 : get8l(attr,8)) == offset) { + if (opt_v >= 2) + printf("Offset 0x%lx (correct)\n",(long)offset); + } else { + printf("** offset 0x%llx (expected : 0x%llx)\n", + (long long)get8l(attr,8), + (long long)(second ? get8l(attr,8) - 0x40000 : get8l(attr,8))); +// unsane = TRUE; + errors++; + } + } + if (!unsane) { + key = get4l(attr,4); + if (opt_v >= 2) + printf("Key 0x%x\n",(int)key); + if (key) { + if (key <= prevkey) { + printf("** Unordered key 0x%lx after 0x%lx\n", + (long)key,(long)prevkey); + unsane = TRUE; + errors++; + } + } + } + } + return (!unsane); +} + +/* + * Check whether a SDS entry is consistent with other known data + * and store current data for subsequent checks + */ + +int consist_sds(const char *attr, unsigned int offset, + unsigned int entrysz, BOOL second) +{ + int errcnt; + u32 key; + u32 comphash; + struct SECURITY_DATA *psecurdata; + + errcnt = 0; + key = get4l(attr,4); + if ((key > 0) && (key < MAXSECURID)) { + printf("Valid entry at 0x%lx for key 0x%lx\n", + (long)offset,(long)key); + if (!securdata[key >> SECBLKSZ]) + newblock(key); + if (securdata[key >> SECBLKSZ]) { + psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)]; + comphash = hash((const le32*)&attr[20],entrysz-20); + if (psecurdata->flags & INSDS1) { + if (psecurdata->hash != comphash) { + printf("** Different hash values : $SDS-1 0x%08lx $SDS-2 0x%08lx\n", + (unsigned long)psecurdata->hash, + (unsigned long)comphash); + errcnt++; + errors++; + } + if (psecurdata->offset != get8l(attr,8)) { + printf("** Different offsets : $SDS-1 0x%llx $SDS-2 0x%llx\n", + (long long)psecurdata->offset,(long long)get8l(attr,8)); + errcnt++; + errors++; + } + if (psecurdata->length != get4l(attr,16)) { + printf("** Different lengths : $SDS-1 0x%lx $SDS-2 0x%lx\n", + (long)psecurdata->length,(long)get4l(attr,16)); + errcnt++; + errors++; + } + } else { + if (second) { + printf("** Entry was not present in $SDS-1\n"); + errcnt++; + errors++; + } + psecurdata->hash = comphash; + psecurdata->offset = get8l(attr,8); + psecurdata->length = get4l(attr,16); + } + psecurdata->flags |= (second ? INSDS2 : INSDS1); + } + } else + if (key || get4l(attr,0)) { + printf("** Security_id 0x%x out of bounds\n",key); + warnings++; + } + return (errcnt); +} + + +/* + * Auditing of $SDS (Linux only) + */ + +int audit_sds(BOOL second) +{ + static char attr[MAXATTRSZ + 20]; + BOOL isdir; + BOOL done; + BOOL unsane; + u32 prevkey; + int errcnt; + int size; + unsigned int entrysz; + unsigned int entryalsz; + unsigned int offset; + int count; + int deleted; + int mode; + + if (second) + printf("\nAuditing $SDS-2\n"); + else + printf("\nAuditing $SDS-1\n"); + errcnt = 0; + offset = (second ? 0x40000 : 0); + count = 0; + deleted = 0; + done = FALSE; + prevkey = 0; + + /* get size of first record */ + + size = ntfs_read_sds(ntfs_context,(char*)attr,20,offset); + if (size != 20) { + if ((size < 0) && (errno == ENOTSUP)) + printf("** There is no $SDS-%d in this volume\n", + (second ? 2 : 1)); + else { + printf("** Could not open $SDS-%d, size %d\n", + (second ? 2 : 1),size); + errors++; + errcnt++; + } + } else + do { + entrysz = get4l(attr,16); + entryalsz = ((entrysz - 1) | 15) + 1; + if (entryalsz <= (MAXATTRSZ + 20)) { + /* read next header in anticipation, to get its size */ + size = ntfs_read_sds(ntfs_context, + (char*)&attr[20],entryalsz,offset + 20); + if (opt_v) + printf("\nAt offset 0x%lx got %lu bytes\n",(long)offset,(long)size); + } else { + printf("** Security attribute is too long (%ld bytes) - stopping\n", + (long)entryalsz); + errcnt++; + } + if ((entryalsz > (MAXATTRSZ + 20)) || (size < (int)(entrysz - 20))) + done = TRUE; + else { + if (opt_v) { + printf("Entry size %d bytes\n",entrysz); + hexdump(&attr[20],size,8); + } + + unsane = !valid_sds(attr,offset,entrysz, + size,prevkey,second); + if (!unsane) { + if (!get4l(attr,0) && !get4l(attr,4)) + deleted++; + else + count++; + errcnt += consist_sds(attr,offset, + entrysz, second); + if (opt_v >= 2) { + isdir = guess_dir(&attr[20]); + printf("Assuming %s descriptor\n",(isdir ? "directory" : "file")); + showheader(&attr[20],0); + showusid(&attr[20],0); + showgsid(&attr[20],0); + showdacl(&attr[20],isdir,0); + showsacl(&attr[20],isdir,0); + showownership(&attr[20]); + mode = linux_permissions( + &attr[20],isdir); + printf("Interpreted Unix mode 0%03o\n",mode); + } + prevkey = get4l(attr,4); + } + if (!unsane) { + memcpy(attr,&attr[entryalsz],20); + offset += entryalsz; + if (!get4l(attr,16) + || ((((offset - 1) | 0x3ffff) - offset + 1) < 20)) { + if (second) + offset = ((offset - 1) | 0x7ffff) + 0x40001; + else + offset = ((offset - 1) | 0x7ffff) + 1; + if (opt_v) + printf("Trying next SDS-%d block at offset 0x%lx\n", + (second ? 2 : 1), (long)offset); + size = ntfs_read_sds(ntfs_context, + (char*)attr,20,offset); + if (size != 20) { + if (opt_v) + printf("Assuming end of $SDS, got %d bytes\n",size); + done = TRUE; + } + } + } else { + printf("** Sanity check failed - stopping there\n"); + errcnt++; + errors++; + done = TRUE; + } + } + } while (!done); + if (count || deleted || errcnt) { + printf("%d valid and %d deleted entries in $SDS-%d\n", + count,deleted,(second ? 2 : 1)); + printf("%d errors in $SDS-%c\n",errcnt,(second ? '2' : '1')); + } + return (errcnt); +} + +/* + * Check whether a SII entry is sane + */ + +BOOL valid_sii(const char *entry, u32 prevkey) +{ + BOOL valid; + u32 key; + + valid = TRUE; + key = get4l(entry,16); + if (key <= prevkey) { + printf("** Unordered key 0x%lx after 0x%lx\n", + (long)key,(long)prevkey); + valid = FALSE; + errors++; + } + prevkey = key; + if (get2l(entry,0) != 20) { + printf("** offset %d (instead of 20)\n",(int)get2l(entry,0)); + valid = FALSE; + errors++; + } + if (get2l(entry,2) != 20) { + printf("** size %d (instead of 20)\n",(int)get2l(entry,2)); + valid = FALSE; + errors++; + } + if (get4l(entry,4) != 0) { + printf("** fill1 %d (instead of 0)\n",(int)get4l(entry,4)); + valid = FALSE; + errors++; + } + if (get2l(entry,12) & 1) { + if (get2l(entry,8) != 48) { + printf("** index size %d (instead of 48)\n",(int)get2l(entry,8)); + valid = FALSE; + errors++; + } + } else + if (get2l(entry,8) != 40) { + printf("** index size %d (instead of 40)\n",(int)get2l(entry,8)); + valid = FALSE; + errors++; + } + if (get2l(entry,10) != 4) { + printf("** index key size %d (instead of 4)\n",(int)get2l(entry,10)); + valid = FALSE; + errors++; + } + if ((get2l(entry,12) & ~3) != 0) { + printf("** flags 0x%x (instead of < 4)\n",(int)get2l(entry,12)); + valid = FALSE; + errors++; + } + if (get2l(entry,14) != 0) { + printf("** fill2 %d (instead of 0)\n",(int)get2l(entry,14)); + valid = FALSE; + errors++; + } + if (get4l(entry,24) != key) { + printf("** key 0x%x (instead of 0x%x)\n", + (int)get4l(entry,24),(int)key); + valid = FALSE; + errors++; + } + return (valid); +} + +/* + * Check whether a SII entry is consistent with other known data + */ + +int consist_sii(const char *entry) +{ + int errcnt; + u32 key; + struct SECURITY_DATA *psecurdata; + + errcnt = 0; + key = get4l(entry,16); + if ((key > 0) && (key < MAXSECURID)) { + printf("Valid entry for key 0x%lx\n",(long)key); + if (!securdata[key >> SECBLKSZ]) + newblock(key); + if (securdata[key >> SECBLKSZ]) { + psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)]; + psecurdata->flags |= INSII; + if (psecurdata->flags & (INSDS1 | INSDS2)) { + if ((u32)get4l(entry,20) != psecurdata->hash) { + printf("** hash 0x%x (instead of 0x%x)\n", + (unsigned int)get4l(entry,20), + (unsigned int)psecurdata->hash); + errors++; + } + if (get8l(entry,28) != psecurdata->offset) { + printf("** offset 0x%llx (instead of 0x%llx)\n", + (long long)get8l(entry,28), + (long long)psecurdata->offset); + errors++; + } + if (get4l(entry,36) != psecurdata->length) { + printf("** length 0x%lx (instead of %ld)\n", + (long)get4l(entry,36), + (long)psecurdata->length); + errors++; + } + } else { + printf("** Entry was not present in $SDS\n"); + errors++; + psecurdata->hash = get4l(entry,20); + psecurdata->offset = get8l(entry,28); + psecurdata->length = get4l(entry,36); + if (opt_v) { + printf(" hash 0x%x\n",(unsigned int)psecurdata->hash); + printf(" offset 0x%llx\n",(long long)psecurdata->offset); + printf(" length %ld\n",(long)psecurdata->length); + } + errcnt++; + } + } + } else { + printf("** Security_id 0x%x out of bounds\n",key); + warnings++; + } + return (errcnt); +} + + +/* + * Auditing of $SII (Linux only) + */ + +int audit_sii() +{ + char *entry; + int errcnt; + u32 prevkey; + BOOL valid; + BOOL done; + int count; + + printf("\nAuditing $SII\n"); + errcnt = 0; + count = 0; + entry = (char*)NULL; + prevkey = 0; + done = FALSE; + do { + entry = (char*)ntfs_read_sii(ntfs_context,(void*)entry); + if (entry) { + valid = valid_sii(entry,prevkey); + if (valid) { + count++; + errcnt += consist_sii(entry); + prevkey = get4l(entry,16); + } else + errcnt++; + } else + if ((errno == ENOTSUP) && !prevkey) + printf("** There is no $SII in this volume\n"); + } while (entry && !done); + if (count || errcnt) { + printf("%d valid entries in $SII\n",count); + printf("%d errors in $SII\n",errcnt); + } + return (errcnt); +} + +/* + * Check whether a SII entry is sane + */ + +BOOL valid_sdh(const char *entry, u32 prevkey, u32 prevhash) +{ + BOOL valid; + u32 key; + u32 currhash; + + valid = TRUE; + currhash = get4l(entry,16); + key = get4l(entry,20); + if ((currhash < prevhash) + || ((currhash == prevhash) && (key <= prevkey))) { + printf("** Unordered hash and key 0x%x 0x%x after 0x%x 0x%x\n", + (unsigned int)currhash,(unsigned int)key, + (unsigned int)prevhash,(unsigned int)prevkey); + valid = FALSE; + errors++; + } + if ((opt_v >= 2) && (currhash == prevhash)) + printf("Hash collision (not an error)\n"); + + if (get2l(entry,0) != 24) { + printf("** offset %d (instead of 24)\n",(int)get2l(entry,0)); + valid = FALSE; + errors++; + } + if (get2l(entry,2) != 20) { + printf("** size %d (instead of 20)\n",(int)get2l(entry,2)); + valid = FALSE; + errors++; + } + if (get4l(entry,4) != 0) { + printf("** fill1 %d (instead of 0)\n",(int)get4l(entry,4)); + valid = FALSE; + errors++; + } + if (get2l(entry,12) & 1) { + if (get2l(entry,8) != 56) { + printf("** index size %d (instead of 56)\n",(int)get2l(entry,8)); + valid = FALSE; + errors++; + } + } else + if (get2l(entry,8) != 48) { + printf("** index size %d (instead of 48)\n",(int)get2l(entry,8)); + valid = FALSE; + errors++; + } + if (get2l(entry,10) != 8) { + printf("** index key size %d (instead of 8)\n",(int)get2l(entry,10)); + valid = FALSE; + errors++; + } + if ((get2l(entry,12) & ~3) != 0) { + printf("** flags 0x%x (instead of < 4)\n",(int)get2l(entry,12)); + valid = FALSE; + errors++; + } + if (get2l(entry,14) != 0) { + printf("** fill2 %d (instead of 0)\n",(int)get2l(entry,14)); + valid = FALSE; + errors++; + } + if ((u32)get4l(entry,24) != currhash) { + printf("** hash 0x%x (instead of 0x%x)\n", + (unsigned int)get4l(entry,24),(unsigned int)currhash); + valid = FALSE; + errors++; + } + if (get4l(entry,28) != key) { + printf("** key 0x%x (instead of 0x%x)\n", + (int)get4l(entry,28),(int)key); + valid = FALSE; + errors++; + } + if (get4l(entry,44) + && (get4l(entry,44) != 0x490049)) { + printf("** fill3 0x%lx (instead of 0 or 0x490049)\n", + (long)get4l(entry,44)); + valid = FALSE; + errors++; + } + return (valid); +} + +/* + * Check whether a SDH entry is consistent with other known data + */ + +int consist_sdh(const char *entry) +{ + int errcnt; + u32 key; + struct SECURITY_DATA *psecurdata; + + errcnt = 0; + key = get4l(entry,20); + if ((key > 0) && (key < MAXSECURID)) { + printf("Valid entry for key 0x%lx\n",(long)key); + if (!securdata[key >> SECBLKSZ]) + newblock(key); + if (securdata[key >> SECBLKSZ]) { + psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)]; + psecurdata->flags |= INSDH; + if (psecurdata->flags & (INSDS1 | INSDS2 | INSII)) { + if ((u32)get4l(entry,24) != psecurdata->hash) { + printf("** hash 0x%x (instead of 0x%x)\n", + (unsigned int)get4l(entry,24), + (unsigned int)psecurdata->hash); + errors++; + } + if (get8l(entry,32) != psecurdata->offset) { + printf("** offset 0x%llx (instead of 0x%llx)\n", + (long long)get8l(entry,32), + (long long)psecurdata->offset); + errors++; + } + if (get4l(entry,40) != psecurdata->length) { + printf("** length %ld (instead of %ld)\n", + (long)get4l(entry,40), + (long)psecurdata->length); + errors++; + } + } else { + printf("** Entry was not present in $SDS nor in $SII\n"); + errors++; + psecurdata->hash = get4l(entry,24); + psecurdata->offset = get8l(entry,32); + psecurdata->length = get4l(entry,40); + if (opt_v) { + printf(" offset 0x%llx\n",(long long)psecurdata->offset); + printf(" length %ld\n",(long)psecurdata->length); + } + errcnt++; + } + } + } else { + printf("** Security_id 0x%x out of bounds\n",key); + warnings++; + } + return (errcnt); +} + +/* + * Auditing of $SDH (Linux only) + */ + +int audit_sdh() +{ + char *entry; + int errcnt; + int count; + u32 prevkey; + u32 prevhash; + BOOL valid; + BOOL done; + + printf("\nAuditing $SDH\n"); + count = 0; + errcnt = 0; + prevkey = 0; + prevhash = 0; + entry = (char*)NULL; + done = FALSE; + do { + entry = (char*)ntfs_read_sdh(ntfs_context,(void*)entry); + if (entry) { + valid = valid_sdh(entry,prevkey,prevhash); + if (valid) { + count++; + errcnt += consist_sdh(entry); + prevhash = get4l(entry,16); + prevkey = get4l(entry,20); + } else + errcnt++; + } else + if ((errno == ENOTSUP) && !prevkey) + printf("** There is no $SDH in this volume\n"); + } while (entry && !done); + if (count || errcnt) { + printf("%d valid entries in $SDH\n",count); + printf("%d errors in $SDH\n",errcnt); + } + return (errcnt); +} + +/* + * Audit summary + */ + +void audit_summary() +{ + int count; + int flags; + int cnt; + int found; + int i,j; + + count = 0; + found = 0; + if (opt_r) printf("Summary of security key use :\n"); + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + if (securdata[i]) + for (j=0; j<(1 << SECBLKSZ); j++) { + flags = securdata[i][j].flags & (INSDS1 + INSDS2 + INSII + INSDH); + if (flags) found++; + if (flags + && (flags != (INSDS1 + INSDS2 + INSII + INSDH))) + { + if (!count && !opt_r) + printf("\n** Keys not present in all files :\n"); + cnt = securdata[i][j].filecount; + if (opt_r) + printf("Key 0x%x used by %d %s, not in", + i*(1 << SECBLKSZ)+j,cnt, + (cnt > 1 ? "files" : "file")); + else + printf("Key 0x%x not in", i*(1 << SECBLKSZ)+j); + if (!(flags & INSDS1)) + printf(" SDS-1"); + if (!(flags & INSDS2)) + printf(" SDS-2"); + if (!(flags & INSII)) + printf(" SII"); + if (!(flags & INSDH)) + printf(" SDH"); + printf("\n"); + count++; + } else { + cnt = securdata[i][j].filecount; + if (opt_r && cnt) + printf("Key 0x%x used by %d %s\n", + i*(1 << SECBLKSZ)+j,cnt, + (cnt > 1 ? "files" : "file")); + } + } + if (found) { + if (count) + printf("%d keys not present in all lists\n",count); + else + printf("All keys are present in all lists\n"); + } +} + +/* + * Auditing (Linux only) + */ + +BOOL audit(const char *volume) +{ + BOOL err; + + err = FALSE; + if (!getuid() && open_security_api()) { + if (open_volume(volume,NTFS_MNT_RDONLY)) { + if (audit_sds(FALSE)) err = TRUE; + if (audit_sds(TRUE)) err = TRUE; + if (audit_sii()) err = TRUE; + if (audit_sdh()) err = TRUE; + if (opt_r) recurseshow("/"); + + audit_summary(); + close_volume(volume); + } + else { + fprintf(stderr,"Could not open volume %s\n",volume); + printerror(stdout); + err = TRUE; + } + close_security_api(); + } + else { + if (getuid()) + fprintf(stderr,"This is only possible as root\n"); + else fprintf(stderr,"Could not open security API\n"); + err = TRUE; + } + return (err); +} + +#endif + +#if POSIXACLS + +/* + * Encode a Posix ACL string + * [d:]{ugmo}:uid[:perms],... + */ + +struct POSIX_SECURITY *encode_posix_acl(const char *str) +{ + int acccnt; + int defcnt; + int i,k,l; + int c; + s32 id; + u16 perms; + u16 apermsset; + u16 dpermsset; + u16 tag; + u16 tagsset; + mode_t mode; + BOOL defacl; + BOOL dmask; + BOOL amask; + const char *p; + struct POSIX_ACL *acl; + struct POSIX_SECURITY *pxdesc; + enum { PXBEGIN, PXTAG, PXTAG1, PXID, PXID1, PXID2, + PXPERM, PXPERM1, PXPERM2, PXOCT, PXNEXT, PXEND, PXERR + } state; + + /* raw evaluation of ACE count */ + p = str; + amask = FALSE; + dmask = FALSE; + if (*p == 'd') { + acccnt = 0; + defcnt = 1; + } else { + if ((*p >= '0') && (*p <= '7')) + acccnt = 0; + else + acccnt = 1; + defcnt = 0; + } + while (*p) + if (*p++ == ',') { + if (*p == 'd') { + defcnt++; + if (p[1] && (p[2] == 'm')) + dmask = TRUE; + } else { + acccnt++; + if (*p == 'm') + amask = TRUE; + } + } + /* account for an implicit mask if none defined */ + if (acccnt && !amask) + acccnt++; + if (defcnt && !dmask) + defcnt++; + pxdesc = (struct POSIX_SECURITY*)malloc(sizeof(struct POSIX_SECURITY) + + (acccnt + defcnt)*sizeof(struct POSIX_ACE)); + if (pxdesc) { + pxdesc->acccnt = acccnt; + pxdesc->firstdef = acccnt; + pxdesc->defcnt = defcnt; + acl = &pxdesc->acl; + p = str; + state = PXBEGIN; + id = 0; + defacl = FALSE; + mode = 0; + apermsset = 0; + dpermsset = 0; + tag = 0; + perms = 0; + k = l = 0; + c = *p++; + while ((state != PXEND) && (state != PXERR)) { + switch (state) { + case PXBEGIN : + if (c == 'd') { + defacl = TRUE; + state = PXTAG1; + break; + } else + if ((c >= '0') && (c <= '7')) { + mode = c - '0'; + state = PXOCT; + break; + } + defacl = FALSE; + /* fall through */ + case PXTAG : + switch (c) { + case 'u' : + tag = POSIX_ACL_USER; + state = PXID; + break; + case 'g' : + tag = POSIX_ACL_GROUP; + state = PXID; + break; + case 'o' : + tag = POSIX_ACL_OTHER; + state = PXID; + break; + case 'm' : + tag = POSIX_ACL_MASK; + state = PXID; + break; + default : + state = PXERR; + break; + } + break; + case PXTAG1 : + if (c == ':') + state = PXTAG; + else + state = PXERR; + break; + case PXID : + if (c == ':') { + if ((tag == POSIX_ACL_OTHER) + || (tag == POSIX_ACL_MASK)) + state = PXPERM; + else + state = PXID1; + } else + state = PXERR; + break; + case PXID1 : + if ((c >= '0') && (c <= '9')) { + id = c - '0'; + state = PXID2; + } else + if (c == ':') { + id = -1; + if (tag == POSIX_ACL_USER) + tag = POSIX_ACL_USER_OBJ; + if (tag == POSIX_ACL_GROUP) + tag = POSIX_ACL_GROUP_OBJ; + state = PXPERM1; + } else + state = PXERR; + break; + case PXID2 : + if ((c >= '0') && (c <= '9')) + id = 10*id + c - '0'; + else + if (c == ':') + state = PXPERM1; + else + state = PXERR; + break; + case PXPERM : + if (c == ':') { + id = -1; + state = PXPERM1; + } else + state = PXERR; + break; + case PXPERM1 : + if ((c >= '0') && (c <= '7')) { + perms = c - '0'; + state = PXNEXT; + break; + } + state = PXPERM2; + perms = 0; + /* fall through */ + case PXPERM2 : + switch (c) { + case 'r' : + perms |= POSIX_PERM_R; + break; + case 'w' : + perms |= POSIX_PERM_W; + break; + case 'x' : + perms |= POSIX_PERM_X; + break; + case ',' : + case '\0' : + if (defacl) { + i = acccnt + l++; + dpermsset |= perms; + } else { + i = k++; + apermsset |= perms; + } + acl->ace[i].tag = tag; + acl->ace[i].perms = perms; + acl->ace[i].id = id; + if (c == '\0') + state = PXEND; + else + state = PXBEGIN; + break; + } + break; + case PXNEXT : + if (!c || (c == ',')) { + if (defacl) { + i = acccnt + l++; + dpermsset |= perms; + } else { + i = k++; + apermsset |= perms; + } + acl->ace[i].tag = tag; + acl->ace[i].perms = perms; + acl->ace[i].id = id; + if (c == '\0') + state = PXEND; + else + state = PXBEGIN; + } else + state = PXERR; + break; + case PXOCT : + if ((c >= '0') && (c <= '7')) + mode = (mode << 3) + c - '0'; + else + if (c == '\0') + state = PXEND; + else + state = PXBEGIN; + break; + default : + break; + } + c = *p++; + } + /* insert default mask if none defined */ + if (acccnt && !amask) { + i = k++; + acl->ace[i].tag = POSIX_ACL_MASK; + acl->ace[i].perms = apermsset; + acl->ace[i].id = -1; + } + if (defcnt && !dmask) { + i = acccnt + l++; + acl->ace[i].tag = POSIX_ACL_MASK; + acl->ace[i].perms = dpermsset; + acl->ace[i].id = -1; + } + /* compute the mode and tagsset */ + tagsset = 0; + for (i=0; i<acccnt; i++) + tagsset |= acl->ace[i].tag; + switch (acl->ace[i].tag) { + case POSIX_ACL_USER_OBJ : + mode |= acl->ace[i].perms << 6; + break; + case POSIX_ACL_GROUP_OBJ : + mode |= acl->ace[i].perms << 3; + break; + case POSIX_ACL_OTHER : + mode |= acl->ace[i].perms; + break; + default : + break; + } + pxdesc->mode = mode; + pxdesc->tagsset = tagsset; + pxdesc->acl.version = POSIX_VERSION; + pxdesc->acl.flags = 0; + pxdesc->acl.filler = 0; + if (state != PXERR) + ntfs_sort_posix(pxdesc); +showposix(pxdesc); + if ((state == PXERR) + || (k != acccnt) + || (l != defcnt) + || !ntfs_valid_posix(pxdesc)) { + if (~pxdesc->tagsset + & (POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER)) + fprintf(stderr,"User, group or other permissions missing\n"); + else + fprintf(stderr,"Bad ACL description\n"); + free(pxdesc); + pxdesc = (struct POSIX_SECURITY*)NULL; + } else + if (opt_v >= 2) { + printf("Interpreted input description :\n"); + showposix(pxdesc); + } + } else + errno = ENOMEM; + return (pxdesc); +} + +#endif /* POSIXACLS */ + + +int getoptions(int argc, char *argv[]) +{ + int xarg; + int narg; + const char *parg; + BOOL err; + + opt_a = FALSE; + opt_b = FALSE; + opt_e = FALSE; + opt_h = FALSE; +#if FORCEMASK + opt_m = FALSE; +#endif + opt_r = FALSE; + opt_s = FALSE; +#if SELFTESTS & !USESTUBS + opt_t = FALSE; +#endif + opt_u = FALSE; + opt_v = 0; + xarg = 1; + err = FALSE; + while ((xarg < argc) && (argv[xarg][0] == '-')) { + parg = argv[xarg++]; + while (*++parg) + switch (*parg) + { +#ifndef WIN32 + case 'a' : + opt_a = TRUE; + break; +#endif + case 'b' : + opt_b = TRUE; + break; + case 'e' : + opt_e = TRUE; + break; + case 'h' : + opt_h = TRUE; + break; +#if FORCEMASK + case 'm' : + opt_m = TRUE; + break; +#endif + case 'r' : + case 'R' : + opt_r = TRUE; + break; + case 's' : + opt_s = TRUE; + break; +#if SELFTESTS & !USESTUBS + case 't' : + opt_t = TRUE; + break; +#endif + case 'u' : + opt_u = TRUE; + break; + case 'v' : + opt_v++; + break; + default : + fprintf(stderr,"Invalid option -%c\n",*parg); + err = TRUE; + } + } + narg = argc - xarg; +#ifdef WIN32 + if ( ((opt_h || opt_s) && (narg > 1)) + || ((opt_r || opt_b || opt_u) && ((narg < 1) || (narg > 2))) +#if SELFTESTS & !USESTUBS + || (opt_t && (narg > 0)) +#endif + || (opt_e && !opt_s) + || (!opt_h && !opt_r && !opt_b && !opt_s +#if SELFTESTS & !USESTUBS + && !opt_t +#endif + && ((narg < 1) || (narg > 2)))) + + err = TRUE; + if (err) { + xarg = 0; + fprintf(stderr,"Usage:\n"); +#if SELFTESTS & !USESTUBS + fprintf(stderr," secaudit -t\n"); + fprintf(stderr," run self-tests\n"); +#endif + fprintf(stderr," secaudit -h [file]\n"); + fprintf(stderr," display security descriptors within file\n"); + fprintf(stderr," secaudit [-v] file\n"); + fprintf(stderr," display the security parameters of file\n"); + fprintf(stderr," secaudit -r[v] directory\n"); + fprintf(stderr," display the security parameters of files in directory\n"); + fprintf(stderr," secaudit -b[v] directory\n"); + fprintf(stderr," backup the security parameters of files in directory\n"); + fprintf(stderr," secaudit -s[ev] [backupfile]\n"); + fprintf(stderr," set the security parameters as indicated in backup file\n"); + fprintf(stderr," with -e also set extra parameters (Windows attrib)\n"); + fprintf(stderr," secaudit perms file\n"); + fprintf(stderr," set the security parameters of file to perms\n"); + fprintf(stderr," secaudit -r[v] perms directory\n"); + fprintf(stderr," set the security parameters of files in directory to perms\n"); + fprintf(stderr," secaudit -u file\n"); + fprintf(stderr," get a user mapping proposal applicable to file\n"); +#if POSIXACLS + fprintf(stderr," Note: perms can be an octal mode or a Posix ACL description\n"); +#else + fprintf(stderr," Note: perms is an octal mode\n"); +#endif + fprintf(stderr," -v is for verbose, -vv for very verbose\n"); + } +#else + if ( (opt_h && (narg > 1)) + || (opt_a && (narg != 1)) + || ((opt_r || opt_b || opt_s || opt_u) + && ((narg < 1) || (narg > 3))) +#if SELFTESTS & !USESTUBS + || (opt_t && (narg > 0)) +#endif + || (opt_e && !opt_s) + || (!opt_h && !opt_a && !opt_r && !opt_b && !opt_s && !opt_u +#if SELFTESTS & !USESTUBS + && !opt_t +#endif +#ifdef HAVE_SETXATTR + && ((narg < 1) || (narg > 3)))) +#else + && ((narg < 2) || (narg > 3)))) +#endif + err = TRUE; + if (err) { + xarg = 0; + fprintf(stderr,"Usage:\n"); +#if SELFTESTS & !USESTUBS + fprintf(stderr," secaudit -t\n"); + fprintf(stderr," run self-tests\n"); +#endif + fprintf(stderr," secaudit -h [file]\n"); + fprintf(stderr," display security descriptors within file\n"); + fprintf(stderr," secaudit -a[rv] volume\n"); + fprintf(stderr," audit the volume\n"); + fprintf(stderr," secaudit [-v] volume file\n"); + fprintf(stderr," display the security parameters of file\n"); + fprintf(stderr," secaudit -r[v] volume directory\n"); + fprintf(stderr," display the security parameters of files in directory\n"); + fprintf(stderr," secaudit -b[v] volume directory\n"); + fprintf(stderr," backup the security parameters of files in directory\n"); + fprintf(stderr," secaudit -s[ev] volume [backupfile]\n"); + fprintf(stderr," set the security parameters as indicated in backup file\n"); + fprintf(stderr," with -e also set extra parameters (Windows attrib)\n"); + fprintf(stderr," secaudit volume perms file\n"); + fprintf(stderr," set the security parameters of file to perms\n"); + fprintf(stderr," secaudit -r[v] volume perms directory\n"); + fprintf(stderr," set the security parameters of files in directory to perms\n"); + fprintf(stderr," secaudit -u volume file\n"); + fprintf(stderr," get a user mapping proposal applicable to file\n"); +#ifdef HAVE_SETXATTR + fprintf(stderr," special cases, do not require being root :\n"); + fprintf(stderr," secaudit -u mounted-file\n"); + fprintf(stderr," get a user mapping proposal applicable to mounted file\n"); + fprintf(stderr," secaudit [-v] mounted-file\n"); + fprintf(stderr," display the security parameters of a mounted file\n"); +#endif +#if POSIXACLS + fprintf(stderr," Note: perms can be an octal mode or a Posix ACL description\n"); +#else + fprintf(stderr," Note: perms is an octal mode\n"); +#endif + fprintf(stderr," -v is for verbose, -vv for very verbose\n"); + } +#endif + if ((sizeof(SID) != 12) && !err) { + fprintf(stderr,"Possible alignment problem, check your compiler options\n"); + err = TRUE; + xarg = 0; + } + return (xarg); +} + +/* + * Memory allocation with checks + */ + +#undef malloc +#undef calloc +#undef free +#undef isalloc + +void dumpalloc(const char *txt) +{ + struct CHKALLOC *q; + + if (firstalloc) { + printf("alloc table at %s\n",txt); + for (q=firstalloc; q; q=q->next) + printf("%08lx : %u bytes at %08lx allocated at %s line %d\n", + (long)q,(unsigned int)q->size, + (long)q->alloc,q->file,q->line); + } +} + +void *chkmalloc(size_t size, const char *file, int line) +{ + void *p; + struct CHKALLOC *q; + + p = (void*)malloc(size+1); + if (p) { + ((unsigned char*)p)[size] = 0xaa; + q = (struct CHKALLOC*)malloc(sizeof(struct CHKALLOC)); + if (q) { + q->next = firstalloc; + q->alloc = p; + q->size = size; + q->file = file; + q->line = line; + firstalloc = q; + } + } + return (p); +} + +void *chkcalloc(size_t cnt, size_t size, const char *file, int line) +{ + return (chkmalloc(cnt*size,file,line)); +} + +void chkfree(void *p, const char *file, int line) +{ + struct CHKALLOC *q; + struct CHKALLOC *r; + + if (p) { + if (firstalloc && (firstalloc->alloc == p)) { + r = firstalloc; + firstalloc = firstalloc->next; + } else { + q = firstalloc; + if (q) + while (q->next && (q->next->alloc != p)) + q = q->next; + if (q && q->next) { + r = q->next; + q->next = r->next; + } else { + r = (struct CHKALLOC*)NULL; + printf("** freeing unallocated memory in %s line %d\n",file,line); + if (!isatty(1)) + fprintf(stderr,"** freeing unallocated memory in %s line %d\n",file,line); + } + } + if (r) { + if (((unsigned char*)p)[r->size] != 0xaa) { + printf("** memory corruption, alloc in %s line %d release in %s %d\n", + r->file,r->line,file,line); + if (!isatty(1)) + fprintf(stderr,"** memory corruption, alloc in %s line %d release in %s %d\n", + r->file,r->line,file,line); + } + memset(p,0xaa,r->size); + free(r); + free(p); + } + } +} + +void *stdmalloc(size_t size) +{ + return (malloc(size)); +} + +void stdfree(void *p) +{ + free(p); +} + +BOOL chkisalloc(void *p, const char *file, int line) +{ + struct CHKALLOC *q; + + if (p) { + q = firstalloc; + while (q && (q->alloc != p)) + q = q->next; + } else + q = (struct CHKALLOC*)NULL; + if (!p || !q) { + printf("error in %s %d : 0x%lx not allocated\n",file,line,(long)p); + } + return (p && q); +} + + + + +#ifdef WIN32 + +/* + * Windows version + */ + +main(argc,argv) +int argc; +char *argv[]; +{ + FILE *fd; + int xarg; + int mode; + unsigned int size; + BOOL cmderr; + char *filename; + const char *p; + int i; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#endif + + printf("%s\n",BANNER); + cmderr = FALSE; + errors = 0; + warnings = 0; + xarg = getoptions(argc,argv); + if (xarg) { + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + securdata[i] = (struct SECURITY_DATA*)NULL; +#if POSIXACLS + context.mapping[MAPUSERS] = (struct MAPPING*)NULL; + context.mapping[MAPGROUPS] = (struct MAPPING*)NULL; +#endif + firstalloc = (struct CHKALLOC*)NULL; + mappingtype = MAPNONE; + switch (argc - xarg) { + case 0 : + if (opt_h) + showhex(stdin); + else + if (opt_s) + restore(stdin); +#if SELFTESTS & !USESTUBS + if (opt_t) + selftests(); +#endif + break; + case 1 : + if (opt_h || opt_s) { + fd = fopen(argv[xarg],"r"); + if (fd) { + if (opt_h) + showhex(fd); + else + restore(fd); + fclose(fd); + } else { + fprintf(stderr,"Could not open %s\n",argv[xarg]); + cmderr = TRUE; + } + } else { + size = utf16size(argv[xarg]); + if (size) { + filename = (char*)malloc(2*size + 2); + if (filename) { + makeutf16(filename,argv[xarg]); + if (opt_u) { + cmderr = mapproposal(filename); + } else { +#if POSIXACLS + if (local_build_mapping(context.mapping,filename)) { + printf("*** Could not get user mapping data\n"); + warnings++; + } +#endif + if (opt_b) + cmderr = backup(filename); + else { + if (opt_r) + cmderr = listfiles(filename); + else + cmderr = singleshow(filename); + } +#if POSIXACLS + ntfs_free_mapping(context.mapping); +#endif + } + free(filename); + } else { + fprintf(stderr,"No more memory\n"); + cmderr = TRUE; + } + } else { + fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg]); + cmderr = TRUE; + } + } + break; + case 2 : + mode = 0; + p = argv[xarg]; +#if POSIXACLS + pxdesc = encode_posix_acl(p); + if (pxdesc) { + size = utf16size(argv[xarg + 1]); + if (size) { + filename = (char*)malloc(2*size + 2); + if (filename) { + makeutf16(filename,argv[xarg + 1]); + if (local_build_mapping(context.mapping,filename)) { + printf("*** Could not get user mapping data\n"); + warnings++; + } + if (opt_r) { + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + securdata[i] = (struct SECURITY_DATA*)NULL; + recurseset_posix(filename,pxdesc); + } else + singleset_posix(filename,pxdesc); + ntfs_free_mapping(context.mapping); + free(filename); + } else { + fprintf(stderr,"No more memory\n"); + cmderr = TRUE; + } + chkfree(pxdesc,__FILE__,__LINE__); + } else { + fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg + 1]); + cmderr = TRUE; + } + } +#else + while ((*p >= '0') && (*p <= '7')) + mode = (mode << 3) + (*p++) - '0'; + if (*p) { + fprintf(stderr,"New mode should be given in octal\n"); + cmderr = TRUE; + } else { + size = utf16size(argv[xarg + 1]); + if (size) { + filename = (char*)malloc(2*size + 2); + if (filename) { + makeutf16(filename,argv[xarg + 1]); +#if POSIXACLS + if (local_build_mapping(&context,filename)) { + printf("*** Could not get user mapping data\n"); + warnings++; + } +#endif + if (opt_r) { + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + securdata[i] = (struct SECURITY_DATA*)NULL; + recurseset(filename,mode); + } else + singleset(filename,mode); + free(filename); + } else { + fprintf(stderr,"No more memory\n"); + cmderr = TRUE; + } + } else { + fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg + 1]); + cmderr = TRUE; + } + } +#endif + break; +#if FORCEMASK + case 3 : + mode = 0; + forcemsk = 0; + p = argv[xarg]; + while (*p) { + if ((*p >= '0') && (*p <= '9')) + forcemsk = (forcemsk << 4) + *p - '0'; + else forcemsk = (forcemsk << 4) + (*p & 7) + 9; + p++; + } + p = argv[xarg + 1]; + while ((*p >= '0') && (*p <= '7')) + mode = (mode << 3) + (*p++) - '0'; + if (*p) { + fprintf(stderr,"New mode should be given in octal\n"); + cmderr = TRUE; + } else { + if (opt_r) { + recurseset(argv[xarg + 2],mode); + } + else singleset(argv[xarg + 2],mode); + } + break; +#endif + } + if (warnings) + printf("** %u %s signalled\n",warnings, + (warnings > 1 ? "warnings were" : "warning was")); + if (errors) + printf("** %u %s found\n",errors, + (errors > 1 ? "errors were" : "error was")); + else + if (!cmderr) + printf("No errors were found\n"); + if (!isatty(1)) { + fflush(stdout); + if (warnings) + fprintf(stderr,"** %u %s signalled\n",warnings, + (warnings > 1 ? "warnings were" : "warning was")); + if (errors) + fprintf(stderr,"** %u %s found\n",errors, + (errors > 1 ? "errors were" : "error was")); + else + fprintf(stderr,"No errors were found\n"); + freeblocks(); + } + } + dumpalloc("termination"); + if (cmderr || errors) + exit(1); + return (0); +} + +#else + +/* + * Linux version + */ + +int main(int argc, char *argv[]) +{ + FILE *fd; + const char *p; + int xarg; + BOOL cmderr; + int i; +#if POSIXACLS + struct POSIX_SECURITY *pxdesc; +#else + unsigned int mode; +#endif + + printf("%s\n",BANNER); + cmderr = FALSE; + errors = 0; + warnings = 0; + xarg = getoptions(argc,argv); + if (xarg) { + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + securdata[i] = (struct SECURITY_DATA*)NULL; +#if POSIXACLS + context.mapping[MAPUSERS] = (struct MAPPING*)NULL; + context.mapping[MAPGROUPS] = (struct MAPPING*)NULL; +#endif + firstalloc = (struct CHKALLOC*)NULL; + mappingtype = MAPNONE; + switch (argc - xarg) { + case 0 : + if (opt_h) + showhex(stdin); +#if SELFTESTS & !USESTUBS + if (opt_t) + selftests(); +#endif + break; + case 1 : + if (opt_a) + cmderr = audit(argv[xarg]); + else + if (opt_h) { + fd = fopen(argv[xarg],"rb"); + if (fd) { + showhex(fd); + fclose(fd); + } else { + fprintf(stderr,"Could not open %s\n",argv[xarg]); + cmderr = TRUE; + } + } else + if (opt_b) + cmderr = backup(argv[xarg],"/"); + else + if (opt_r) + cmderr = listfiles(argv[xarg],"/"); + else + if (opt_s) + cmderr = dorestore(argv[xarg],stdin); + else + cmderr = processmounted(argv[xarg]); + break; + case 2 : + if (opt_b) + cmderr = backup(argv[xarg],argv[xarg+1]); + else + if (opt_s) { + fd = fopen(argv[xarg+1],"rb"); + if (fd) { + if (dorestore(argv[xarg],fd)) + cmderr = TRUE; + fclose(fd); + } else { + fprintf(stderr,"Could not open %s\n",argv[xarg]); + cmderr = TRUE; + } + } else + if (opt_u) + cmderr = mapproposal(argv[xarg],argv[xarg+1]); + else + cmderr = listfiles(argv[xarg],argv[xarg+1]); + break; + case 3 : + p = argv[xarg+1]; +#if POSIXACLS + pxdesc = encode_posix_acl(p); + if (pxdesc) { + if (!getuid() && open_security_api()) { + if (open_volume(argv[xarg],NTFS_MNT_NONE)) { + if (opt_r) { + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + securdata[i] = (struct SECURITY_DATA*)NULL; + recurseset_posix(argv[xarg + 2],pxdesc); + } else + singleset_posix(argv[xarg + 2],pxdesc); + close_volume(argv[xarg]); + } else { + fprintf(stderr,"Could not open volume %s\n",argv[xarg]); + printerror(stderr); + cmderr = TRUE; + } + close_security_api(); + } else { + if (getuid()) + fprintf(stderr,"This is only possible as root\n"); + else + fprintf(stderr,"Could not open security API\n"); + cmderr = TRUE; + } + chkfree(pxdesc,__FILE__,__LINE__); + } else + cmderr = TRUE; +#else + mode = 0; + while ((*p >= '0') && (*p <= '7')) + mode = (mode << 3) + (*p++) - '0'; + if (*p) { + fprintf(stderr,"New mode should be given in octal\n"); + cmderr = TRUE; + } else + if (!getuid() && open_security_api()) { + if (open_volume(argv[xarg],NTFS_MNT_NONE)) { + if (opt_r) { + for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++) + securdata[i] = (struct SECURITY_DATA*)NULL; + recurseset(argv[xarg + 2],mode); + } else + singleset(argv[xarg + 2],mode); + close_volume(argv[xarg]); + } else { + fprintf(stderr,"Could not open volume %s\n",argv[xarg]); + printerror(stderr); + cmderr = TRUE; + } + close_security_api(); + } else { + if (getuid()) + fprintf(stderr,"This is only possible as root\n"); + else + fprintf(stderr,"Could not open security API\n"); + cmderr = TRUE; + } +#endif + break; + } + if (warnings) + printf("** %u %s signalled\n",warnings, + (warnings > 1 ? "warnings were" : "warning was")); + if (errors) + printf("** %u %s found\n",errors, + (errors > 1 ? "errors were" : "error was")); + else + if (!cmderr) + printf("No errors were found\n"); + if (!isatty(1)) { + fflush(stdout); + if (warnings) + fprintf(stderr,"** %u %s signalled\n",warnings, + (warnings > 1 ? "warnings were" : "warning was")); + if (errors) + fprintf(stderr,"** %u %s found\n",errors, + (errors > 1 ? "errors were" : "error was")); + else + if (!cmderr) + fprintf(stderr,"No errors were found\n"); + } + freeblocks(); + } else + cmderr = TRUE; + dumpalloc("termination"); + if (cmderr || errors) + exit(1); + return (0); +} + +#endif diff --git a/src/secaudit.h b/src/secaudit.h new file mode 100755 index 0000000000000000000000000000000000000000..a8ad163f92a19c9bb0097cf8a4594cb6af3bd2e9 --- /dev/null +++ b/src/secaudit.h @@ -0,0 +1,737 @@ +/* + * General declarations for secaudit + * + * These declarations are organized to enable code sharing with ntfs-3g + * library, but should only be used to build tools runnable both + * on Linux (dynamic linking) and Windows (static linking) + * + * Copyright (c) 2007-2009 Jean-Pierre Andre + * + */ + +/* + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * General parameters which may have to be adapted to needs + */ + +#define SELFTESTS 1 /* include code for self-testing */ +#define POSIXACLS 0 /* include code for processing Posix ACLs */ +#define NOREVBOM 0 /* temporary */ + +#define OWNERFROMACL 1 /* must match option in security.c */ + +#define MAXATTRSZ 65536 /* Max sec attr size (16448 met for WinXP) */ +#define MAXSECURID 262144 +#define SECBLKSZ 8 +#define MAXFILENAME 4096 +#define FORCEMASK 0 /* Special (dangerous) option -m to force a mask */ +#define MAXLINE 80 /* maximum processed size of a line */ +#define BUFSZ 1024 /* buffer size to read mapping file */ +#define LINESZ 120 /* maximum useful size of a mapping line */ + +/* + * Definitions for Linux + * Use explicit or implicit dynamic linking + */ + +#ifdef HAVE_CONFIG_H +#undef POSIXACLS /* override default by configure option */ +#define USESTUBS 1 /* API stubs generated at link time */ +#else +#define USESTUBS 0 /* direct calls to API, based on following definitions */ +#define ENVNTFS3G "NTFS3G" +#define LIBFILE64 "/lib64/libntfs-3g.so.4921" +#define LIBFILE "/lib/libntfs-3g.so.4921" +#endif + +#define MAPDIR ".NTFS-3G" +#define MAPFILE "UserMapping" +#define MAGIC_API 0x09042009 + +#ifndef _NTFS_ENDIANS_H + +typedef char s8; +typedef short s16; +typedef long long s64; +typedef unsigned char u8; +typedef unsigned short le16, be16, u16; +typedef unsigned long long u64; +#ifdef STSC +typedef long s32; +typedef unsigned long le32, be32, u32; +#else +typedef int s32; +typedef unsigned int le32, be32, u32; +#endif + +#ifdef STSC +#define endian_rev16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L)) +#define endian_rev32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \ + + (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L)) +#else +#define endian_rev16(x) ((((x) & 255) << 8) + (((x) >> 8) & 255)) +#define endian_rev32(x) ((((x) & 255) << 24) + (((x) & 0xff00) << 8) \ + + (((x) >> 8) & 0xff00) + (((x) >> 24) & 255)) +#endif +#define endian_rev64(x) ((((x) & 255LL) << 56) + (((x) & 0xff00LL) << 40) \ + + (((x) & 0xff0000LL) << 24) + (((x) & 0xff000000LL) << 8) \ + + (((x) >> 8) & 0xff000000LL) + (((x) >> 24) & 0xff0000LL) \ + + (((x) >> 40) & 0xff00LL) + (((x) >> 56) & 255LL)) + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +#define cpu_to_be16(x) endian_rev16(x) +#define cpu_to_be32(x) endian_rev32(x) +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le64(x) (x) +#define le16_to_cpu(x) (x) +#define le32_to_cpu(x) (x) +#define le64_to_cpu(x) (x) + +#else + +#define cpu_to_be16(x) (x) +#define cpu_to_be32(x) (x) +#define cpu_to_le16(x) endian_rev16(x) +#define cpu_to_le32(x) endian_rev32(x) +#define cpu_to_le64(x) endian_rev64(x) +#define le16_to_cpu(x) endian_rev16(x) +#define le32_to_cpu(x) endian_rev32(x) +#define le64_to_cpu(x) endian_rev64(x) + +#endif + +#define const_le16_to_cpu(x) le16_to_cpu(x) +#define const_cpu_to_le16(x) cpu_to_le16(x) +#define const_cpu_to_le32(x) cpu_to_le32(x) +#define const_cpu_to_be16(x) cpu_to_be16(x) +#define const_cpu_to_be32(x) cpu_to_be32(x) + +#endif /* _NTFS_ENDIANS_H */ + +#ifndef FALSE +enum { FALSE, TRUE } ; +#endif /* FALSE */ + +#ifdef WIN32 + +typedef unsigned short uid_t; +typedef unsigned short gid_t; + +#define UNICODE(c) ((unsigned short)(c)) + +#define __attribute__(x) + +#else + +#ifndef BOOL +typedef int BOOL; /* Already defined in windows.h */ +#endif /* BOOL */ + +#ifdef STSC + +#define ENOTSUP 95 + +#endif /* STSC */ + +typedef u32 DWORD; /* must be 32 bits whatever the platform */ +typedef DWORD *LPDWORD; + +#define NTFS_MNT_NONE 0 /* no flag for mounting the device */ +#define NTFS_MNT_RDONLY 1 /* flag for mounting the device read-only */ + +#endif /* WIN32 */ + +#if defined(WIN32) | defined(STSC) + +/* + * On non-Linux computers, there is no mount and the user mapping + * if fetched from a real file (or a dummy one for self tests) + */ + +#define NTFS_FIND_USID(map,uid,buf) ntfs_find_usid(map,uid,buf) +#define NTFS_FIND_GSID(map,gid,buf) ntfs_find_gsid(map,gid,buf) +#define NTFS_FIND_USER(map,usid) ntfs_find_user(map,usid) +#define NTFS_FIND_GROUP(map,gsid) ntfs_find_group(map,gsid) + +#else + +/* + * On Linux computers, there is a mount and the user mapping + * if either obtained through the mount process or fetched + * from a dummy file for self-tests + */ + +#define NTFS_FIND_USID(map,uid,buf) (mappingtype != MAPEXTERN ? \ + ntfs_find_usid(map,uid,buf) : relay_find_usid(map,uid,buf)) +#define NTFS_FIND_GSID(map,gid,buf) (mappingtype != MAPEXTERN ? \ + ntfs_find_gsid(map,gid,buf) : relay_find_gsid(map,gid,buf)) +#define NTFS_FIND_USER(map,usid) (mappingtype != MAPEXTERN ? \ + ntfs_find_user(map,usid) : relay_find_user(map,usid)) +#define NTFS_FIND_GROUP(map,gsid) (mappingtype != MAPEXTERN ? \ + ntfs_find_group(map,gsid) : relay_find_group(map,gsid)) + +#endif + +/* + * A few name hijackings or definitions + * needed for using code from ntfs-3g + */ + +#ifdef WIN32 +#define ACL MY_ACL +#define SID MY_SID +#define ACCESS_ALLOWED_ACE MY_ACCESS_ALLOWED_ACE +#define ACCESS_DENIED_ACE MY_ACCESS_DENIED_ACE +#define FILE_ATTRIBUTE_REPARSE_POINT 0x400 +#define IO_REPARSE_TAG_MOUNT_POINT 0xa0000003 +#define IO_REPARSE_TAG_SYMLINK 0xa000000c +#else +#define SE_OWNER_DEFAULTED const_cpu_to_le16(1) +#define SE_GROUP_DEFAULTED const_cpu_to_le16(2) +#define SE_DACL_PRESENT const_cpu_to_le16(4) +#define SE_SACL_PRESENT const_cpu_to_le16(0x10) +#define SE_DACL_DEFAULTED const_cpu_to_le16(8) +#define SE_SELF_RELATIVE const_cpu_to_le16(0x8000) +#define SID_REVISION 1 +#endif /* WIN32 */ +#define SE_DACL_PROTECTED const_cpu_to_le16(0x1000) +#define SE_SACL_PROTECTED const_cpu_to_le16(0x2000) +#define SE_DACL_AUTO_INHERITED const_cpu_to_le16(0x400) +#define SE_SACL_AUTO_INHERITED const_cpu_to_le16(0x800) +#define SE_DACL_AUTO_INHERIT_REQ cpu_to_le16(0x100) +#define SE_SACL_AUTO_INHERIT_REQ cpu_to_le16(0x200) + +typedef le16 ntfschar; + +#define ntfs_log_error(args...) do { printf("** " args); if (!isatty(1)) fprintf(stderr,args); } while(0) + +/* + * Struct to hold the input mapping file + * (private to this module) + */ + +struct MAPLIST { + struct MAPLIST *next; + char *uidstr; /* uid text from the same record */ + char *gidstr; /* gid text from the same record */ + char *sidstr; /* sid text from the same record */ + char maptext[LINESZ + 1]; +}; + +/* + * A few dummy declarations needed for using code from security.c + */ + +#define MFT_RECORD_IS_DIRECTORY const_cpu_to_le16(1) + +struct SECURITY_DATA { + u64 offset; + char *attr; + u32 hash; + u32 length; + unsigned int filecount:16; + unsigned int mode:12; + unsigned int flags:4; +} ; + + /* default security sub-authorities */ +enum { + DEFSECAUTH1 = -1153374643, /* 3141592653 */ + DEFSECAUTH2 = 589793238, + DEFSECAUTH3 = 462843383, + DEFSECBASE = 10000 +}; + +#define OWNERID 1016 +#define GROUPID 513 + + +#define INSDS1 1 +#define INSDS2 2 +#define INSII 4 +#define INSDH 8 + +#ifdef WIN32 + +typedef enum { RECSHOW, RECSET, RECSETPOSIX } RECURSE; + +#endif + +/* + * A type large enough to hold any SID + */ + +typedef char BIGSID[40]; + +/* + * Declarations for memory allocation checks + */ + +struct CHKALLOC + { + struct CHKALLOC *next; + void *alloc; + const char *file; + int line; + size_t size; + } ; + +#if defined(WIN32) | defined(STSC) + +#define S_ISVTX 01000 +#define S_ISGID 02000 +#define S_ISUID 04000 +#define S_IXUSR 0100 +#define S_IWUSR 0200 +#define S_IRUSR 0400 +#define S_IXGRP 010 +#define S_IWGRP 020 +#define S_IRGRP 040 +#define S_IXOTH 001 +#define S_IWOTH 002 +#define S_IROTH 004 + +#endif + +#ifdef WIN32 +#else +/* + * + * See http://msdn2.microsoft.com/en-us/library/aa379649.aspx + */ + +typedef enum { + DACL_SECURITY_INFORMATION = 4, // The DACL of the object is being referenced. + SACL_SECURITY_INFORMATION = 8, // The SACL of the object is being referenced. + LABEL_SECURITY_INFORMATION = 8, // The mandatory integrity label is being referenced. + GROUP_SECURITY_INFORMATION = 2, // The primary group identifier of the object is being referenced. + OWNER_SECURITY_INFORMATION = 1, // The owner identifier of the object is being referenced. +} SECURITY_INFORMATION; + +#define STANDARD_RIGHTS_READ cpu_to_le32(0x20000) +#define STANDARD_RIGHTS_WRITE cpu_to_le32(0x20000) +#define STANDARD_RIGHTS_EXECUTE cpu_to_le32(0x20000) +#define STANDARD_RIGHTS_REQUIRED cpu_to_le32(0xf0000) + +#endif + +typedef struct SECHEAD { + s8 revision; + s8 alignment; + le16 control; + le32 owner; + le32 group; + le32 sacl; + le32 dacl; +} SECURITY_DESCRIPTOR_RELATIVE; + +typedef struct ACL { + s8 revision; + s8 alignment1; + le16 size; + le16 ace_count; + le16 alignment2; +} ACL; + +typedef struct { + union { + struct { + unsigned char revision; + unsigned char sub_authority_count; + } ; + struct { + /* evade an alignment problem when a 4 byte field */ + /* in a struct implies alignment of the struct */ + le16 dummy; + be16 high_part; + be32 low_part; + } identifier_authority; + } ; + le32 sub_authority[1]; +} SID; + +typedef u8 ACE_FLAGS; + +typedef struct ACE { + u8 type; + u8 flags; + le16 size; + le32 mask; + SID sid; +} ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE; + + +/* + * item in the mapping list + */ + +struct MAPPING { + struct MAPPING *next; + int xid; /* linux id : uid or gid */ + SID *sid; /* Windows id : usid or gsid */ + int grcnt; /* group count (for users only) */ + gid_t *groups; /* groups which the user is member of */ +}; + +/* + * Posix ACL structures + */ + +struct POSIX_ACE { + u16 tag; + u16 perms; + s32 id; +} ; + +struct POSIX_ACL { + u8 version; + u8 flags; + u16 filler; + struct POSIX_ACE ace[0]; +} ; + +struct POSIX_SECURITY { + mode_t mode; + int acccnt; + int defcnt; + int firstdef; + u16 tagsset; + struct POSIX_ACL acl; +} ; + +/* + * Posix tags, cpu-endian 16 bits + */ + +enum { + POSIX_ACL_USER_OBJ = 1, + POSIX_ACL_USER = 2, + POSIX_ACL_GROUP_OBJ = 4, + POSIX_ACL_GROUP = 8, + POSIX_ACL_MASK = 16, + POSIX_ACL_OTHER = 32, + POSIX_ACL_SPECIAL = 64 /* internal use only */ +} ; + +/* + * Posix permissions, cpu-endian 16 bits + */ + +enum { + POSIX_PERM_X = 1, + POSIX_PERM_W = 2, + POSIX_PERM_R = 4, + POSIX_PERM_DENIAL = 64 /* internal use only */ +} ; + +#define POSIX_VERSION 2 + +/* + * A few definitions adapted from winnt.h + * (Windows version uses actual definitions from winnt.h, which are + * not compatible with code from security.c on a big-endian computer) + */ + +#ifndef WIN32 + +#define DELETE cpu_to_le32(0x00010000L) +#define READ_CONTROL cpu_to_le32(0x00020000L) +#define WRITE_DAC cpu_to_le32(0x00040000L) +#define WRITE_OWNER cpu_to_le32(0x00080000L) +#define SYNCHRONIZE cpu_to_le32(0x00100000L) + + +#define FILE_READ_DATA cpu_to_le32( 0x0001 ) // file & pipe +#define FILE_LIST_DIRECTORY cpu_to_le32( 0x0001 ) // directory + +#define FILE_WRITE_DATA cpu_to_le32( 0x0002 ) // file & pipe +#define FILE_ADD_FILE cpu_to_le32( 0x0002 ) // directory + +#define FILE_APPEND_DATA cpu_to_le32( 0x0004 ) // file +#define FILE_ADD_SUBDIRECTORY cpu_to_le32( 0x0004 ) // directory +#define FILE_CREATE_PIPE_INSTANCE cpu_to_le32( 0x0004 ) // named pipe + + +#define FILE_READ_EA cpu_to_le32( 0x0008 ) // file & directory + +#define FILE_WRITE_EA cpu_to_le32( 0x0010 ) // file & directory + +#define FILE_EXECUTE cpu_to_le32( 0x0020 ) // file +#define FILE_TRAVERSE cpu_to_le32( 0x0020 ) // directory + +#define FILE_DELETE_CHILD cpu_to_le32( 0x0040 ) // directory + +#define FILE_READ_ATTRIBUTES cpu_to_le32( 0x0080 ) // all + +#define FILE_WRITE_ATTRIBUTES cpu_to_le32( 0x0100 ) // all + +#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | \ + cpu_to_le32(0x1FF)) + +#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ |\ + FILE_READ_DATA |\ + FILE_READ_ATTRIBUTES |\ + FILE_READ_EA |\ + SYNCHRONIZE) + + +#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE |\ + FILE_WRITE_DATA |\ + FILE_WRITE_ATTRIBUTES |\ + FILE_WRITE_EA |\ + FILE_APPEND_DATA |\ + SYNCHRONIZE) + + +#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE |\ + FILE_READ_ATTRIBUTES |\ + FILE_EXECUTE |\ + SYNCHRONIZE) + +#define GENERIC_READ cpu_to_le32(0x80000000L) +#define GENERIC_WRITE cpu_to_le32(0x40000000L) +#define GENERIC_EXECUTE cpu_to_le32(0x20000000L) +#define GENERIC_ALL cpu_to_le32(0x10000000L) + + +#define OBJECT_INHERIT_ACE (0x1) +#define CONTAINER_INHERIT_ACE (0x2) +#define NO_PROPAGATE_INHERIT_ACE (0x4) +#define INHERIT_ONLY_ACE (0x8) +#define INHERITED_ACE (0x10) +#define VALID_INHERIT_FLAGS (0x1F) + +/* + * Other useful definitions + */ + +#define ACL_REVISION 2 +#define ACCESS_ALLOWED_ACE_TYPE 0 +#define ACCESS_DENIED_ACE_TYPE 1 +#define SECURITY_DESCRIPTOR_REVISION 1 + +#endif /* !WIN32 */ + +#ifndef ACL_REVISION_DS /* not always defined in <windows.h> */ +#define ACL_REVISION_DS 4 +#endif + +#ifndef INHERITED_ACE /* not always defined in <windows.h> */ +#define INHERITED_ACE (0x10) +#undef VALID_INHERIT_FLAGS +#define VALID_INHERIT_FLAGS (0x1F) +#endif + +/* + * Matching of ntfs permissions to Linux permissions + * these constants are adapted to endianness + * when setting, set them all + * when checking, check one is present + */ + + /* flags which are set to mean exec, write or read */ + +#define FILE_READ (FILE_READ_DATA) +#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) +#define FILE_EXEC (FILE_EXECUTE) +#define DIR_READ FILE_LIST_DIRECTORY +#define DIR_WRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD \ + | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) +#define DIR_EXEC (FILE_TRAVERSE) + + /* flags tested for meaning exec, write or read */ + /* tests for write allow for interpretation of a sticky bit */ + +#define FILE_GREAD (FILE_READ_DATA | GENERIC_READ | GENERIC_ALL) +#define FILE_GWRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE \ + | GENERIC_ALL) +#define FILE_GEXEC (FILE_EXECUTE | GENERIC_EXECUTE | GENERIC_ALL) +#define DIR_GREAD (FILE_LIST_DIRECTORY | GENERIC_READ | GENERIC_ALL) +#define DIR_GWRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | GENERIC_WRITE \ + | GENERIC_ALL) +#define DIR_GEXEC (FILE_TRAVERSE | GENERIC_EXECUTE | GENERIC_ALL) + + /* standard owner (and administrator) rights */ + +#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \ + | SYNCHRONIZE \ + | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \ + | FILE_READ_EA | FILE_WRITE_EA) + + /* standard world rights */ + +#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \ + | SYNCHRONIZE) + + /* inheritance flags for files and directories */ + +#define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE +#define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE) + +/* + * To identify NTFS ACL meaning Posix ACL granted to root + * we use rights always granted to anybody, so they have no impact + * either on Windows or on Linux. + */ + +#define ROOT_OWNER_UNMARK SYNCHRONIZE /* ACL granted to root as owner */ +#define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */ + + +struct SII { /* this is an image of an $SII index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keysecurid; + + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; /* documented as badly aligned */ + le32 dataoffsh; + le32 datasize; +} ; + +struct SDH { /* this is an image of an $SDH index entry */ + le16 offs; + le16 size; + le32 fill1; + le16 indexsz; + le16 indexksz; + le16 flags; + le16 fill2; + le32 keyhash; + le32 keysecurid; + + /* did not find official description for the following */ + le32 hash; + le32 securid; + le32 dataoffsl; + le32 dataoffsh; + le32 datasize; + le32 fill3; + } ; + +#ifndef INVALID_FILE_ATTRIBUTES /* not defined in old windows.h */ +#define INVALID_FILE_ATTRIBUTES (-1) +#endif + +enum { MAPUSERS, MAPGROUPS, MAPCOUNT } ; + +struct SECURITY_CONTEXT { + struct MAPPING *mapping[MAPCOUNT]; +} ; + +typedef enum { MAPNONE, MAPEXTERN, MAPLOCAL, MAPDUMMY } MAPTYPE; + + + +struct passwd { + uid_t pw_uid; +} ; + +struct group { + gid_t gr_gid; +} ; + +typedef int (*FILEREADER)(void *fileid, char *buf, size_t size, off_t pos); + +/* + * Data defined in secaudit.c + */ + +extern MAPTYPE mappingtype; + +/* + * Functions defined in acls.c + */ + +BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz); +BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc); +BOOL ntfs_valid_pattern(const SID *sid); +BOOL ntfs_same_sid(const SID *first, const SID *second); + + +int ntfs_sid_size(const SID * sid); +unsigned int ntfs_attr_size(const char *attr); + +const SID *ntfs_find_usid(const struct MAPPING *usermapping, + uid_t uid, SID *pdefsid); +const SID *ntfs_find_gsid(const struct MAPPING *groupmapping, + gid_t gid, SID *pdefsid); +uid_t ntfs_find_user(const struct MAPPING *usermapping, const SID *usid); +gid_t ntfs_find_group(const struct MAPPING *groupmapping, const SID * gsid); +const SID *ntfs_acl_owner(const char *secattr); + +void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc); +int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode); + + +struct POSIX_SECURITY *ntfs_build_permissions_posix( + struct MAPPING* const mapping[], + const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +int ntfs_build_permissions(const char *securattr, + const SID *usid, const SID *gsid, BOOL isdir); +struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid); +struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem); +struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem); +void ntfs_free_mapping(struct MAPPING *mapping[]); + +struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first, + const struct POSIX_SECURITY *second); +char *ntfs_build_descr_posix(struct MAPPING* const mapping[], + struct POSIX_SECURITY *pxdesc, + int isdir, const SID *usid, const SID *gsid); +char *ntfs_build_descr(mode_t mode, + int isdir, const SID * usid, const SID * gsid); + +/* + * Functions defined in secaudit.c + */ + +void *chkmalloc(size_t, const char*, int); +void *chkcalloc(size_t, size_t, const char *, int); +void chkfree(void*, const char*, int); +BOOL chkisalloc(void*, const char*, int); +void dumpalloc(const char*); + +#define malloc(sz) chkmalloc(sz, __FILE__, __LINE__) +#define calloc(cnt,sz) chkcalloc(cnt, sz, __FILE__, __LINE__) +#define free(ptr) chkfree(ptr, __FILE__, __LINE__) +#define isalloc(ptr) chkisalloc(ptr, __FILE__, __LINE__) +#define ntfs_malloc(sz) chkmalloc(sz, __FILE__, __LINE__) + +struct passwd *getpwnam(const char *user); +struct group *getgrnam(const char *group); + +const SID *relay_find_usid(const struct MAPPING *usermapping, + uid_t uid, SID *pdefsid); +const SID *relay_find_gsid(const struct MAPPING *groupmapping, + gid_t gid, SID *pdefsid); +uid_t relay_find_user(const struct MAPPING *usermapping, const SID *usid); +gid_t relay_find_group(const struct MAPPING *groupmapping, const SID * gsid); + diff --git a/src/usermap.c b/src/usermap.c new file mode 100755 index 0000000000000000000000000000000000000000..19c5c9065159bbb64311e0802103d8ae91036ea6 --- /dev/null +++ b/src/usermap.c @@ -0,0 +1,1365 @@ +/* + * Windows to Linux user mapping for ntfs-3g + * + * + * Copyright (c) 2007-2014 Jean-Pierre Andre + * + * A quick'n dirty program scanning owners of files in + * "c:\Documents and Settings" (and "c:\Users") + * and asking user to map them to Linux accounts + * + * History + * + * Sep 2007 + * - first version, limited to Win32 + * + * Oct 2007 + * - ported to Linux (rewritten would be more correct) + * + * Nov 2007 Version 1.0.0 + * - added more defaults + * + * Nov 2007 Version 1.0.1 + * - avoided examining files whose name begin with a '$' + * + * Jan 2008 Version 1.0.2 + * - moved user mapping file to directory .NTFS-3G (hidden for Linux) + * - fixed an error case in Windows version + * + * Nov 2008 Version 1.1.0 + * - fixed recursions for account in Linux version + * - searched owner in c:\Users (standard location for Vista) + * + * May 2009 Version 1.1.1 + * - reordered mapping records to limit usage of same SID for user and group + * - fixed decoding SIDs on 64-bit systems + * - fixed a pointer to dynamic data in mapping tables + * - fixed default mapping on Windows + * - fixed bug for renaming UserMapping on Windows + * + * May 2009 Version 1.1.2 + * - avoided selecting DOS names on Linux + * + * Nov 2009 Version 1.1.3 + * - silenced compiler warnings for unused parameters + * + * Jan 2010 Version 1.1.4 + * - fixed compilation problems for Mac OSX (Erik Larsson) + * + * Apr 2014 Version 1.1.5 + * - displayed the parent directory of selected files + * + * May 2014 Version 1.1.6 + * - fixed a wrong function header + */ + +/* + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * General parameters which may have to be adapted to needs + */ + +#ifdef HAVE_CONFIG_H +#define USESTUBS 1 /* API stubs generated at link time */ +#else +#define USESTUBS 0 /* direct calls to API, based on following definitions */ +#define ENVNTFS3G "NTFS3G" +#define LIBFILE64 "/lib64/libntfs-3g.so.491" +#define LIBFILE "/lib/libntfs-3g.so.491" +#endif + +#define GET_FILE_SECURITY "ntfs_get_file_security" +#define SET_FILE_SECURITY "ntfs_set_file_security" +#define READ_DIRECTORY "ntfs_read_directory" +#define INIT_FILE_SECURITY "ntfs_initialize_file_security" +#define LEAVE_FILE_SECURITY "ntfs_leave_file_security" + +#define VERSION "1.1.6" +#define MAPDIR ".NTFS-3G" +#define MAPFILE "UserMapping" +#define MAXATTRSZ 2048 +#define MAXSIDSZ 80 +#define MAXNAMESZ 256 +#define OWNERS1 "Documents and Settings" +#define OWNERS2 "Users" + +/* + * Define WIN32 for a Windows execution + * may have to be adapted to compiler or something else + */ + +#ifndef WIN32 +#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC) +#define WIN32 1 +#endif +#endif + +#ifdef WIN32 +#define BANNER "Generated by usermap for Windows, v " VERSION +#else +#define BANNER "Generated by usermap for Linux, v " VERSION +#endif + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +/* + * Define the security API according to platform + */ + +#ifdef WIN32 + +#include <fcntl.h> +#include <windows.h> + +#define STATIC + +typedef enum { DENIED, AGREED } boolean; + +#else + +#include <unistd.h> +#include <dlfcn.h> + +typedef enum { DENIED, AGREED } boolean, BOOL; +typedef unsigned int DWORD; /* must be 32 bits whatever the platform */ +typedef DWORD *LPDWORD; + +enum { OWNER_SECURITY_INFORMATION = 1, + GROUP_SECURITY_INFORMATION = 2, + DACL_SECURITY_INFORMATION = 4, + SACL_SECURITY_INFORMATION = 8 +} ; + +struct CALLBACK { + const char *accname; + const char *dir; + int levels; + int docset; +} ; + +typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname, + int length, int type, long long pos, unsigned long long mft_ref, + unsigned int dt_type); + +#if USESTUBS + +#define STATIC static + +BOOL ntfs_get_file_security(void *scapi, + const char *path, DWORD selection, + char *buf, DWORD buflen, LPDWORD psize); +BOOL ntfs_set_file_security(void *scapi, + const char *path, DWORD selection, const char *attr); +BOOL ntfs_read_directory(void *scapi, + const char *path, dircallback callback, void *context); +void *ntfs_initialize_file_security(const char *device, + unsigned long flags); +BOOL ntfs_leave_file_security(void *scapi); + +#else + +#define STATIC + +BOOL (*ntfs_get_file_security)(void *scapi, + const char *path, DWORD selection, + char *buf, DWORD buflen, LPDWORD psize); +BOOL (*ntfs_set_file_security)(void *scapi, + const char *path, DWORD selection, const char *attr); +BOOL (*ntfs_read_directory)(void *scapi, + const char *path, dircallback callback, void *context); +void *(*ntfs_initialize_file_security)(const char *device, + unsigned long flags); +BOOL (*ntfs_leave_file_security)(void *scapi); + +#endif + +STATIC boolean open_security_api(void); +STATIC boolean close_security_api(void); +STATIC boolean open_volume(const char *volume); +STATIC boolean close_volume(const char *volume); + +#endif + +struct MAPPING { + struct MAPPING *next; + const char *uidstr; + const char *gidstr; + const char *sidstr; + const unsigned char *sid; + const char *login; + boolean defined; +}; + +struct MAPPING *firstmapping; +struct MAPPING *lastmapping; + +#ifdef WIN32 +char *currentwinname; +char *currentdomain; +unsigned char *currentsid; +#endif + +#ifndef WIN32 + +void *ntfs_handle; +void *ntfs_context = (void*)NULL; + +/* + * Shut down compiler warnings for unused parameters + */ + +static long unused(const void *p) +{ +return ((long)p); +} + +/* + * Open and close the security API (platform dependent) + */ + +STATIC boolean open_security_api(void) +{ +#if USESTUBS + return (AGREED); +#else + char *error; + boolean err; + const char *libfile; + + err = AGREED; + libfile = getenv(ENVNTFS3G); + if (!libfile) + libfile = (sizeof(char*) == 8 ? LIBFILE64 : LIBFILE); + ntfs_handle = dlopen(libfile,RTLD_LAZY); + if (ntfs_handle) { + ntfs_initialize_file_security = + dlsym(ntfs_handle,INIT_FILE_SECURITY); + error = dlerror(); + if (error) + fprintf(stderr," %s\n",error); + else { + ntfs_leave_file_security = + dlsym(ntfs_handle,LEAVE_FILE_SECURITY); + ntfs_get_file_security = + dlsym(ntfs_handle,GET_FILE_SECURITY); + ntfs_set_file_security = + dlsym(ntfs_handle,SET_FILE_SECURITY); + ntfs_read_directory = + dlsym(ntfs_handle,READ_DIRECTORY); + err = !ntfs_initialize_file_security + || !ntfs_leave_file_security + || !ntfs_get_file_security + || !ntfs_set_file_security + || !ntfs_read_directory; + if (error) + fprintf(stderr,"ntfs-3g API not available\n"); + } + } else { + fprintf(stderr,"Could not open ntfs-3g library\n"); + fprintf(stderr,"\nPlease set environment variable \"" ENVNTFS3G "\"\n"); + fprintf(stderr,"to appropriate path and retry\n"); + } + return (!err); +#endif +} + +STATIC boolean close_security_api(void) +{ +#if USESTUBS + return (0); +#else + return (!dlclose(ntfs_handle)); +#endif +} + +/* + * Open and close a volume (platform dependent) + * assuming a single volume needs to be opened at any time + */ + +STATIC boolean open_volume(const char *volume) +{ + boolean ok; + + ok = DENIED; + if (!ntfs_context) { + ntfs_context = ntfs_initialize_file_security(volume,0); + if (ntfs_context) { + fprintf(stderr,"\"%s\" opened\n",volume); + ok = AGREED; + } else { + fprintf(stderr,"Could not open \"%s\"\n",volume); + fprintf(stderr,"Make sure \"%s\" is not mounted\n",volume); + } + } else + fprintf(stderr,"A volume is already open\n"); + return (ok); +} + +STATIC boolean close_volume(const char *volume) +{ + boolean r; + + r = ntfs_leave_file_security(ntfs_context); + if (r) + fprintf(stderr,"\"%s\" closed\n",volume); + else + fprintf(stderr,"Could not close \"%s\"\n",volume); + ntfs_context = (void*)NULL; + return (r); +} + +/* + * A poor man's conversion of Unicode to UTF8 + * We are assuming outputs to terminal expect UTF8 + */ + +STATIC void to_utf8(char *dst, const char *src, unsigned int cnt) +{ + unsigned int ch; + unsigned int i; + + for (i=0; i<cnt; i++) { + ch = *src++ & 255; + ch += (*src++ & 255) << 8; + if (ch < 0x80) + *dst++ = ch; + else + if (ch < 0x1000) { + *dst++ = 0xc0 + (ch >> 6); + *dst++ = 0x80 + (ch & 63); + } else { + *dst++ = 0xe0 + (ch >> 12); + *dst++ = 0x80 + ((ch >> 6) & 63); + *dst++ = 0x80 + (ch & 63); + } + } + *dst = 0; +} + +STATIC int utf8_size(const char *src, unsigned int cnt) +{ + unsigned int ch; + unsigned int i; + int size; + + size = 0; + for (i=0; i<cnt; i++) { + ch = *src++ & 255; + ch += (*src++ & 255) << 8; + if (ch < 0x80) + size++; + else + if (ch < 0x1000) + size += 2; + else + size += 3; + } + return (size); +} + +#endif + + +STATIC void welcome(void) +{ + printf("\nThis tool will help you to build a mapping of Windows users\n"); + printf("to Linux users.\n"); + printf("Be prepared to give Linux user id (uid) and group id (gid)\n"); + printf("for owners of files which will be selected.\n"); +} + +STATIC unsigned int get2l(const unsigned char *attr, int p) +{ + int i; + unsigned int v; + + v = 0; + for (i = 0; i < 2; i++) + v += (attr[p + i] & 255) << (8 * i); + return (v); +} + +STATIC unsigned long get4l(const unsigned char *attr, int p) +{ + int i; + unsigned long v; + + v = 0; + for (i = 0; i < 4; i++) + v += (attr[p + i] & 255L) << (8 * i); + return (v); +} + +STATIC unsigned long long get6h(const unsigned char *attr, int p) +{ + int i; + unsigned long long v; + + v = 0; + for (i = 0; i < 6; i++) + v = (v << 8) + (attr[p + i] & 255L); + return (v); +} + +STATIC char *decodesid(const unsigned char *sid) +{ + char *str; + int i; + unsigned long long auth; + unsigned long subauth; + + str = (char *)malloc(MAXSIDSZ); + if (str) { + strcpy(str, "S"); + sprintf(&str[strlen(str)], "-%d", sid[0]); /* revision */ + auth = get6h(sid, 2); +#ifdef WIN32 + sprintf(&str[strlen(str)], "-%I64u", auth); /* main authority */ +#else + sprintf(&str[strlen(str)], "-%llu", auth); /* main authority */ +#endif + for (i = 0; (i < 8) && (i < sid[1]); i++) { + subauth = get4l(sid, 8 + 4 * i); + sprintf(&str[strlen(str)], "-%lu", subauth); /* sub-authority */ + } + } + return (str); +} + +/* + * Test whether a generic group (S-1-5-21- ... -513) + */ + +STATIC boolean isgenericgroup(const char *sid) +{ + boolean yes; + + yes = !strncmp(sid,"S-1-5-21-",9) + && !strcmp(strrchr(sid,'-'),"-513"); + return (yes); +} + +STATIC unsigned char *makegroupsid(const unsigned char *sid) +{ + static unsigned char groupsid[MAXSIDSZ]; + int size; + + size = 8 + 4*sid[1]; + memcpy(groupsid, sid, size); + /* replace last level by 513 */ + groupsid[size - 4] = 1; + groupsid[size - 3] = 2; + groupsid[size - 2] = 0; + groupsid[size - 1] = 0; + return (groupsid); +} + +STATIC void domapping(const char *accname, const char *filename, + const char *dir, const unsigned char *sid, int type) +{ + char buf[81]; + char *sidstr; + char *idstr; + int sidsz; + boolean reject; + struct MAPPING *mapping; + char *login; + char *p; + + if ((get6h(sid, 2) == 5) && (get4l(sid, 8) == 21)) { + sidstr = decodesid(sid); + mapping = firstmapping; + while (mapping && strcmp(mapping->sidstr, sidstr)) + mapping = mapping->next; + if (mapping + && (mapping->defined + || !accname + || !strcmp(mapping->login, accname))) + free(sidstr); /* decision already known */ + else { + do { + reject = DENIED; + printf("\n"); + if (accname) + printf("Under Windows login \"%s\"\n", accname); + if (dir) + printf(" in directory \"%s\"\n",dir); + printf(" file \"%s\" has no mapped %s\n", + filename,(type ? "group" : "owner")); + printf("By which Linux login should this file be owned ?\n"); + printf("Enter %s of login, or just press \"enter\" if this file\n", + (type ? "gid" : "uid")); + printf("does not belong to a user, or you do not known to whom\n"); + printf("\n"); + if (type) + printf("Group : "); + else + printf("User : "); + p = fgets(buf, 80, stdin); + if (p && p[0] && (p[strlen(p) - 1] == '\n')) + p[strlen(p) - 1] = '\0'; + + if (p && p[0] + && ((p[0] == '0') || !strcmp(p, "root"))) { + printf("Please do not map users to root\n"); + printf("Administrators will be mapped automatically\n"); + reject = AGREED; + } + if (reject) + printf("Please retry\n"); + } while (reject); + if (!mapping) { + mapping = + (struct MAPPING *) + malloc(sizeof(struct MAPPING)); + mapping->next = (struct MAPPING *)NULL; + mapping->defined = DENIED; + if (lastmapping) + lastmapping->next = mapping; + else + firstmapping = mapping; + lastmapping = mapping; + } + if (mapping) { + if (p && p[0]) { + idstr = (char *)malloc(strlen(p) + 1); + if (idstr) { + strcpy(idstr, p); + if (type) { + mapping->uidstr = ""; + mapping->gidstr = idstr; + } else { + mapping->uidstr = idstr; + mapping->gidstr = idstr; + } + mapping->defined = AGREED; + } + } + mapping->sidstr = sidstr; + if (accname) { + login = (char*)malloc(strlen(accname) + 1); + if (login) + strcpy(login,accname); + mapping->login = login; + } else + mapping->login = (char*)NULL; + sidsz = 8 + sid[1]*4; + p = (char*)malloc(sidsz); + if (p) { + memcpy(p, sid, sidsz); + } + mapping->sid = (unsigned char*)p; + } + } + } +} + +STATIC void listaclusers(const char *accname, const unsigned char *attr, int off) +{ + int i; + int cnt; + int x; + + cnt = get2l(attr, off + 4); + x = 8; + for (i = 0; i < cnt; i++) { + domapping(accname, (char *)NULL, (char*)NULL, + &attr[off + x + 8], 2); + x += get2l(attr, off + x + 2); + } +} + +#ifdef WIN32 + +STATIC void account(const char *accname, const char *dir, const char *name, int type) +{ + unsigned char attr[MAXATTRSZ]; + unsigned long attrsz; + char *fullname; + int attrib; + + fullname = (char *)malloc(strlen(dir) + strlen(name) + 2); + if (fullname) { + strcpy(fullname, dir); + strcat(fullname, "\\"); + strcat(fullname, name); + attrib = GetFileAttributes(fullname); + if (attrib & 0x10) { /* only directories processed */ + if (GetFileSecurity + (fullname, OWNER_SECURITY_INFORMATION, attr, MAXATTRSZ, + &attrsz)) { + domapping(accname, name, dir, &attr[20], 0); + attrsz = 0; + if (GetFileSecurity + (fullname, GROUP_SECURITY_INFORMATION, attr, + MAXATTRSZ, &attrsz)) + domapping(accname, name, dir, &attr[20], 1); + else + printf(" No group SID\n"); + attrsz = 0; + if (GetFileSecurityA + (fullname, DACL_SECURITY_INFORMATION, attr, + MAXATTRSZ, &attrsz)) { + if (type == 0) + listaclusers(accname, attr, 20); + } else + printf + (" No discretionary access control list\n"); + } + } + free(fullname); + } +} + +#else + +STATIC void account(const char *accname, const char *dir, const char *name, int type) +{ + unsigned char attr[MAXATTRSZ]; + DWORD attrsz; + char *fullname; + + fullname = (char *)malloc(strlen(dir) + strlen(name) + 2); + if (fullname) { + strcpy(fullname, dir); + strcat(fullname, "/"); + strcat(fullname, name); + if (ntfs_get_file_security(ntfs_context, + fullname, OWNER_SECURITY_INFORMATION, + (char*)attr, MAXATTRSZ, &attrsz)) { + domapping(accname, name, dir, &attr[20], 0); + attrsz = 0; + if (ntfs_get_file_security(ntfs_context, + fullname, GROUP_SECURITY_INFORMATION, + (char*)attr, MAXATTRSZ, &attrsz)) + domapping(accname, name, dir, &attr[20], 1); + else + printf(" No group SID\n"); + attrsz = 0; + if (ntfs_get_file_security(ntfs_context, + fullname, DACL_SECURITY_INFORMATION, + (char*)attr, MAXATTRSZ, &attrsz)) { + if (type == 0) + listaclusers(accname, attr, 20); + } else + printf(" No discretionary access control list for %s !\n", + dir); + } + free(fullname); + } +} + +#endif + + +/* + * recursive search of file owners and groups in a directory + */ + +#ifdef WIN32 + +STATIC boolean recurse(const char *accname, const char *dir, int levels) +{ + WIN32_FIND_DATA found; + HANDLE search; + char *filter; + char *fullname; + boolean err; + + err = DENIED; + filter = (char *)malloc(strlen(dir) + 5); + if (filter) { + strcpy(filter, dir); + strcat(filter, "\\*.*"); + search = FindFirstFile(filter, &found); + if (search != INVALID_HANDLE_VALUE) { + do { + if (found.cFileName[0] != '.') { + account(accname, dir, found.cFileName,1); + if (levels > 0) { + fullname = + (char *)malloc(strlen(dir) + + strlen(found.cFileName) + + 2); + if (fullname) { + strcpy(fullname, dir); + strcat(fullname, "\\"); + strcat(fullname, + found.cFileName); + recurse(accname, + fullname, + levels - 1); + free(fullname); + } + } + } + } while (FindNextFile(search, &found)); + FindClose(search); + } + free(filter); + } else { + printf("Directory %s not found\n",dir); + err = AGREED; + } + return (!err); +} + +#else + +STATIC boolean recurse(const char *accname, const char *dir, int levels, int docset); + +STATIC int callback(struct CALLBACK *context, char *ntfsname, + int length, int type, long long pos, unsigned long long mft_ref, + unsigned int dt_type) +{ + char *fullname; + char *accname; + char *name; + + unused((void*)&pos); + unused((void*)&mft_ref); + unused((void*)&dt_type); + fullname = (char *)malloc(strlen(context->dir) + + utf8_size(ntfsname, length) + 2); + if (fullname) { + if (strcmp(context->dir,"/")) { + strcpy(fullname, context->dir); + strcat(fullname, "/"); + } else + strcpy(fullname,"/"); + /* Unicode to ascii conversion by a lazy man */ + name = &fullname[strlen(fullname)]; + to_utf8(name, ntfsname, length); + /* ignore special files and DOS names */ + if ((type != 2) + && strcmp(name,".") + && strcmp(name,"..") + && (name[0] != '$')) { + switch (context->docset) { + case 2 : + /* + * only "Documents and Settings" + * or "Users" + */ + if (!strcmp(name,OWNERS1) + || !strcmp(name,OWNERS2)) { + recurse((char*)NULL, fullname, 2, 1); + } + break; + /* + * within "Documents and Settings" + * or "Users" + */ + case 1 : + accname = (char*)malloc(strlen(name) + 1); + if (accname) { + strcpy(accname, name); + if (context->levels > 0) + recurse(name, fullname, + context->levels - 1, 0); + } + break; + /* + * not related to "Documents and Settings" + * or "Users" + */ + case 0 : + account(context->accname, context->dir, + name, 1); + if (context->levels > 0) + recurse(context->accname, fullname, + context->levels - 1, 0); + break; + } + } + free(fullname); + } +/* check expected return value */ + return (0); +} + +STATIC boolean recurse(const char *accname, const char *dir, int levels, int docset) +{ + struct CALLBACK context; + boolean err; + + err = DENIED; + context.dir = dir; + context.accname = accname; + context.levels = levels; + context.docset = docset; + ntfs_read_directory(ntfs_context,dir,callback,&context); + return (!err); +} +#endif + +/* + * Search directory "Documents and Settings" for user accounts + */ + +#ifdef WIN32 + +STATIC boolean getusers(const char *dir, int levels) +{ + WIN32_FIND_DATA found; + HANDLE search; + char *filter; + char *fullname; + char *accname; + boolean err; + const char *docset; + + /* first get files from "Documents and Settings" */ + err = DENIED; + if (sizeof(OWNERS1) > sizeof(OWNERS2)) + filter = (char *)malloc(strlen(dir) + strlen(OWNERS1) + 6); + else + filter = (char *)malloc(strlen(dir) + strlen(OWNERS2) + 6); + if (filter) { + docset = OWNERS1; + strcpy(filter, dir); + strcat(filter, "\\"); + strcat(filter, docset); + strcat(filter, "\\*.*"); + search = FindFirstFile(filter, &found); + /* if failed, retry with "Users" */ + if (search == INVALID_HANDLE_VALUE) { + docset = OWNERS2; + strcpy(filter, dir); + strcat(filter, "\\"); + strcat(filter, docset); + strcat(filter, "\\*.*"); + search = FindFirstFile(filter, &found); + } + if (search != INVALID_HANDLE_VALUE) { + do { + if (found.cFileName[0] != '.') { + fullname = + (char *)malloc(strlen(dir) + + strlen(docset) + + strlen(found.cFileName) + 3); + accname = (char *) + malloc(strlen(found.cFileName) + 1); + if (fullname && accname) { + strcpy(accname, + found.cFileName); + + strcpy(fullname, dir); + strcat(fullname, "\\"); + strcat(fullname, docset); + strcat(fullname, "\\"); + strcat(fullname, + found.cFileName); + recurse(accname, fullname, 2); + + free(fullname); + } + } + } while (FindNextFile(search, &found)); + FindClose(search); + } else { + printf("No subdirectory found in %s\\%s\n",dir,docset); + } + /* now search in other directories */ + strcpy(filter, dir); + strcat(filter, "\\*.*"); + search = FindFirstFile(filter, &found); + if (search != INVALID_HANDLE_VALUE) { + do { + if ((found.cFileName[0] != '.') + && strcmp(found.cFileName,OWNERS1) + && strcmp(found.cFileName,OWNERS2)) { + fullname = + (char *)malloc(strlen(dir) + + strlen(found.cFileName) + 2); + if (fullname) { + strcpy(fullname, dir); + strcat(fullname, "\\"); + strcat(fullname, + found.cFileName); + recurse((char*)NULL, fullname, 2); + free(fullname); + } + } + } while (FindNextFile(search, &found)); + FindClose(search); + } else { + printf("No directory found in %s\n",dir); + err = AGREED; + } + } + return (!err); +} + +#else + +STATIC boolean getusers(const char *dir, int levels) +{ + boolean err; + struct CALLBACK context; + + printf("* Search for \"" OWNERS1 "\" and \"" OWNERS2 "\"\n"); + err = DENIED; + context.dir = dir; + context.accname = (const char*)NULL; + context.levels = levels; + context.docset = 2; + ntfs_read_directory(ntfs_context,dir,callback,&context); + printf("* Search for other directories %s\n",dir); + context.docset = 0; + ntfs_read_directory(ntfs_context,dir,callback,&context); + + return (!err); +} + +#endif + +#ifdef WIN32 +/* + * Get the current login name (Win32 only) + */ + +STATIC void loginname(boolean silent) +{ + char *winname; + char *domain; + unsigned char *sid; + unsigned long namesz; + unsigned long sidsz; + unsigned long domainsz; + int nametype; + boolean ok; + int r; + + ok = FALSE; + winname = (char*)malloc(MAXNAMESZ); + domain = (char*)malloc(MAXNAMESZ); + sid = (char*)malloc(MAXSIDSZ); + + namesz = MAXNAMESZ; + domainsz = MAXNAMESZ; + sidsz = MAXSIDSZ; + if (winname + && domain + && sid + && GetUserName(winname,&namesz)) { + winname[namesz] = '\0'; + if (!silent) + printf("Your current user name is %s\n",winname); + nametype = 1; + r = LookupAccountName((char*)NULL,winname,sid,&sidsz, + domain,&domainsz,&nametype); + if (r) { + domain[domainsz] = '\0'; + if (!silent) + printf("Your account domain is %s\n",domain); + ok = AGREED; + } + } + if (ok) { + currentwinname = winname; + currentdomain = domain; + currentsid = sid; + } else { + currentwinname = (char*)NULL; + currentdomain = (char*)NULL; + currentsid = (unsigned char*)NULL; + } +} + +/* + * Minimal output on stdout + */ + +boolean minimal(unsigned char *sid) +{ + const unsigned char *groupsid; + boolean ok; + + ok = DENIED; + if (sid) { + groupsid = makegroupsid(sid); + printf("# %s\n",BANNER); + printf("# For Windows account \"%s\" in domain \"%s\"\n", + currentwinname, currentdomain); + printf("# Replace \"user\" and \"group\" hereafter by matching Linux login\n"); + printf("user::%s\n",decodesid(sid)); + printf(":group:%s\n",decodesid(groupsid)); + ok = AGREED; + } + return (ok); +} + +#endif + +STATIC boolean outputmap(const char *volume, const char *dir) +{ + char buf[256]; + int fn; + char *fullname; + char *backup; + struct MAPPING *mapping; + boolean done; + boolean err; + boolean undecided; +#ifdef WIN32 +#else + struct stat st; + int s; +#endif + + done = DENIED; + fullname = (char *)malloc(strlen(MAPFILE) + 1 + + strlen(volume) + 1 + + (dir ? strlen(dir) + 1 : 0)); + if (fullname) { +#ifdef WIN32 + strcpy(fullname, volume); + if (dir && dir[0]) { + strcat(fullname, "\\"); + strcat(fullname,dir); + } + + /* build directory, if not present */ + if (GetFileAttributes(fullname) & 0x80000000) { + printf("* Creating directory %s\n", fullname); + mkdir(fullname); + } + + strcat(fullname, "\\"); + strcat(fullname, MAPFILE); + printf("\n"); + + if (!(GetFileAttributes(fullname) & 0x80000000)) { + backup = (char*)malloc(strlen(fullname) + 5); + strcpy(backup,fullname); + strcat(backup,".bak"); + unlink(backup); + if (!rename(fullname,backup)) + printf("* Old mapping file moved to %s\n",backup); + } +#else + strcpy(fullname, MAPFILE); + printf("\n"); + + s = stat(fullname,&st); + if (!s) { + backup = (char*)malloc(strlen(fullname + 5)); + strcpy(backup,fullname); + strcat(backup,".bak"); + if (rename(fullname,backup)) + printf("* Old mapping file moved to %s\n",backup); + } +#endif + + printf("* Creating file %s\n", fullname); + err = DENIED; +#ifdef WIN32 + fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY + O_BINARY, + S_IREAD + S_IWRITE); +#else + fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY, + S_IREAD + S_IWRITE); +#endif + if (fn > 0) { + sprintf(buf,"# %s\n",BANNER); + if (!write(fn,buf,strlen(buf))) + err = AGREED; + printf("%s",buf); + undecided = DENIED; + /* records for owner only or group only */ + for (mapping = firstmapping; mapping && !err; + mapping = mapping->next) + if (mapping->defined + && (!mapping->uidstr[0] || !mapping->gidstr[0])) { + sprintf(buf,"%s:%s:%s\n", + mapping->uidstr, + mapping->gidstr, + mapping->sidstr); + if (!write(fn,buf,strlen(buf))) + err = AGREED; + printf("%s",buf); + } else + undecided = AGREED; + /* records for both owner and group */ + for (mapping = firstmapping; mapping && !err; + mapping = mapping->next) + if (mapping->defined + && mapping->uidstr[0] && mapping->gidstr[0]) { + sprintf(buf,"%s:%s:%s\n", + mapping->uidstr, + mapping->gidstr, + mapping->sidstr); + if (!write(fn,buf,strlen(buf))) + err = AGREED; + printf("%s",buf); + } else + undecided = AGREED; + done = !err; + close(fn); + if (undecided) { + printf("Undecided :\n"); + for (mapping = firstmapping; mapping; + mapping = mapping->next) + if (!mapping->defined) { + printf(" %s\n", mapping->sidstr); + } + } +#ifndef WIN32 + printf("\n* You will have to move the file \"" MAPFILE "\"\n"); + printf(" to directory \"" MAPDIR "\" after mounting\n"); +#endif + } + } + if (!done) + fprintf(stderr, "* Could not create mapping file \"%s\"\n", fullname); + return (done); +} + +STATIC boolean sanitize(void) +{ + char buf[81]; + boolean ok; + int ownercnt; + int groupcnt; + struct MAPPING *mapping; + struct MAPPING *firstowner; + struct MAPPING *genericgroup; + struct MAPPING *group; + char *sidstr; + + /* count owners and groups */ + /* and find first user, and a generic group */ + ownercnt = 0; + groupcnt = 0; + firstowner = (struct MAPPING*)NULL; + genericgroup = (struct MAPPING*)NULL; + for (mapping=firstmapping; mapping; mapping=mapping->next) { + if (mapping->defined && mapping->uidstr[0]) { + if (!ownercnt) + firstowner = mapping; + ownercnt++; + } + if (mapping->defined && mapping->gidstr[0] && !mapping->uidstr[0]) { + groupcnt++; + } + if (!mapping->defined && isgenericgroup(mapping->sidstr)) { + genericgroup = mapping; + } + } +#ifdef WIN32 + /* no user defined, on Windows, suggest a mapping */ + /* based on account currently used */ + if (!ownercnt && currentwinname && currentsid) { + char *owner; + char *p; + + printf("\nYou have defined no file owner,\n"); + printf(" please enter the Linux login which should be mapped\n"); + printf(" to account you are currently using\n"); + printf(" Linux user ? "); + p = fgets(buf, 80, stdin); + if (p && p[0] && (p[strlen(p) - 1] == '\n')) + p[strlen(p) - 1] = '\0'; + if (p && p[0]) { + firstowner = (struct MAPPING*)malloc(sizeof(struct MAPPING)); + owner = (char*)malloc(strlen(p) + 1); + if (firstowner && owner) { + strcpy(owner, p); + firstowner->next = firstmapping; + firstowner->uidstr = owner; + firstowner->gidstr = ""; + firstowner->sidstr = decodesid(currentsid); + firstowner->sid = currentsid; + firstmapping = firstowner; + ownercnt++; + /* prefer a generic group with the same authorities */ + for (mapping=firstmapping; mapping; + mapping=mapping->next) + if (!mapping->defined + && isgenericgroup(mapping->sidstr) + && !memcmp(firstowner->sidstr, + mapping->sidstr, + strlen(mapping->sidstr)-3)) + genericgroup = mapping; + } + } + } +#endif + if (ownercnt) { + /* + * No group was selected, but there were a generic group + * insist in using it, associated to the first user + */ + if (!groupcnt) { + printf("\nYou have defined no group, this can cause problems\n"); + printf("Do you accept defining a standard group ?\n"); + if (!fgets(buf,80,stdin) + || ((buf[0] != 'n') + && (buf[0] != 'N'))) { + if (genericgroup) { + genericgroup->uidstr = ""; + genericgroup->gidstr = firstowner->uidstr; + genericgroup->defined = AGREED; + } else { + group = (struct MAPPING*) + malloc(sizeof(struct MAPPING)); + sidstr = decodesid( + makegroupsid(firstowner->sid)); + if (group && sidstr) { + group->uidstr = ""; + group->gidstr = firstowner-> + uidstr; + group->sidstr = sidstr; + group->defined = AGREED; + group->next = firstmapping; + firstmapping = group; + } + } + } + } + ok = AGREED; + } else { + printf("\nYou have defined no user, no mapping can be built\n"); + ok = DENIED; + } + + return (ok); +} + +STATIC boolean checkoptions(int argc, char *argv[], boolean silent) +{ + boolean err; +#ifdef WIN32 + int xarg; + const char *pvol; + + if (silent) + err = (argc != 1); + else { + err = (argc < 2); + for (xarg=1; (xarg<argc) && !err; xarg++) { + pvol = argv[xarg]; + if (pvol[0] && (pvol[1] == ':') && !pvol[2]) { + err = !(((pvol[0] >= 'A') && (pvol[0] <= 'Z')) + || ((pvol[0] >= 'a') && (pvol[0] <= 'z'))); + } + } + } + if (err) { + fprintf(stderr, "Usage : usermap [vol1: [vol2: ...]]\n"); + fprintf(stderr, " \"voln\" are the letters of the partition to share with Linux\n"); + fprintf(stderr, " eg C:\n"); + fprintf(stderr, " the Windows system partition should be named first\n"); + } +#else + unused((void*)argv); + unused((void*)&silent); + err = (argc < 2); + if (err) { + fprintf(stderr, "Usage : usermap dev1 [dev2 ...]\n"); + fprintf(stderr, " \"dev.\" are the devices to share with Windows\n"); + fprintf(stderr, " eg /dev/sdb1\n"); + fprintf(stderr, " the devices should not be mounted\n"); + fprintf(stderr, " the Windows system partition should be named first\n"); + } else + if (getuid()) { + fprintf(stderr, "\nSorry, only root can start usermap\n"); + err = AGREED; + } +#endif + return (!err); +} + +STATIC boolean process(int argc, char *argv[]) +{ + boolean ok; + int xarg; + int targ; + + firstmapping = (struct MAPPING *)NULL; + lastmapping = (struct MAPPING *)NULL; + ok = AGREED; +#ifdef WIN32 + for (xarg=1; (xarg<argc) && ok; xarg++) { + printf("\n* Scanning \"%s\" (two levels)\n",argv[xarg]); + ok = getusers(argv[xarg],2); + } +#else + for (xarg=1; (xarg<argc) && ok; xarg++) + if (open_volume(argv[xarg])) { + printf("\n* Scanning \"%s\" (two levels)\n",argv[xarg]); + ok = getusers("/",2); + close_volume(argv[xarg]); + } else + ok = DENIED; +#endif + if (ok && sanitize()) { + targ = (argc > 2 ? 2 : 1); + if (!outputmap(argv[targ],MAPDIR)) { + printf("Trying to write file on root directory\n"); + if (outputmap(argv[targ],(const char*)NULL)) { + printf("\nNote : you will have to move the file to directory \"%s\" on Linux\n", + MAPDIR); + } else + ok = DENIED; + } else + ok = DENIED; + } else + ok = DENIED; + return (ok); +} + +int main(int argc, char *argv[]) +{ + boolean ok; + boolean silent; + + silent = !isatty(1); + if (!silent) + welcome(); + if (checkoptions(argc, argv, silent)) { +#ifdef WIN32 + loginname(silent); + if (silent) + ok = minimal(currentsid); + else + ok = process(argc, argv); +#else + if (open_security_api()) { + ok = process(argc,argv); + if (!close_security_api()) ok = DENIED; + } + else { + ok = DENIED; + } +#endif + } else + ok = DENIED; + if (!ok) + exit(1); + return (0); +} diff --git a/stamp-h1 b/stamp-h1 new file mode 100755 index 0000000000000000000000000000000000000000..4547fe1b5efa99ebbf20e1fa55fdbd528abd3a97 --- /dev/null +++ b/stamp-h1 @@ -0,0 +1 @@ +timestamp for config.h