From 1a780338e1def1df55b6cbe542a1313a40fb4c3b Mon Sep 17 00:00:00 2001 From: Patrick McCarty Date: Tue, 5 Mar 2013 16:04:13 -0800 Subject: Imported Upstream version 3.0.16 --- COPYING | 674 ++++++++++++++++ ChangeLog | 1149 ++++++++++++++++++++++++++++ Makefile | 113 +++ bin/Nindent | 18 + doc/ANNOUNCE.mkdosfs | 41 + doc/ChangeLog.dosfsck | 10 + doc/ChangeLog.dosfstools-2.x | 161 ++++ doc/ChangeLog.mkdosfs | 18 + doc/README.dosfsck | 60 ++ doc/README.dosfstools-2.x | 60 ++ doc/README.mkdosfs | 50 ++ doc/TODO.dosfstools-2.x | 14 + man/dosfsck.8 | 135 ++++ man/dosfslabel.8 | 50 ++ man/mkdosfs.8 | 244 ++++++ src/boot.c | 577 ++++++++++++++ src/boot.h | 32 + src/charconv.c | 59 ++ src/charconv.h | 9 + src/check.c | 1055 +++++++++++++++++++++++++ src/check.h | 40 + src/common.c | 119 +++ src/common.h | 58 ++ src/dosfsck.c | 224 ++++++ src/dosfsck.h | 216 ++++++ src/dosfslabel.c | 144 ++++ src/fat.c | 550 ++++++++++++++ src/fat.h | 85 +++ src/file.c | 286 +++++++ src/file.h | 70 ++ src/io.c | 231 ++++++ src/io.h | 71 ++ src/lfn.c | 524 +++++++++++++ src/lfn.h | 39 + src/mkdosfs.c | 1734 ++++++++++++++++++++++++++++++++++++++++++ src/version.h | 29 + 36 files changed, 8949 insertions(+) create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 Makefile create mode 100755 bin/Nindent create mode 100644 doc/ANNOUNCE.mkdosfs create mode 100644 doc/ChangeLog.dosfsck create mode 100644 doc/ChangeLog.dosfstools-2.x create mode 100644 doc/ChangeLog.mkdosfs create mode 100644 doc/README.dosfsck create mode 100644 doc/README.dosfstools-2.x create mode 100644 doc/README.mkdosfs create mode 100644 doc/TODO.dosfstools-2.x create mode 100644 man/dosfsck.8 create mode 100644 man/dosfslabel.8 create mode 100644 man/mkdosfs.8 create mode 100644 src/boot.c create mode 100644 src/boot.h create mode 100644 src/charconv.c create mode 100644 src/charconv.h create mode 100644 src/check.c create mode 100644 src/check.h create mode 100644 src/common.c create mode 100644 src/common.h create mode 100644 src/dosfsck.c create mode 100644 src/dosfsck.h create mode 100644 src/dosfslabel.c create mode 100644 src/fat.c create mode 100644 src/fat.h create mode 100644 src/file.c create mode 100644 src/file.h create mode 100644 src/io.c create mode 100644 src/io.h create mode 100644 src/lfn.c create mode 100644 src/lfn.h create mode 100644 src/mkdosfs.c create mode 100644 src/version.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..9b1d32c --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1149 @@ +commit ac4d30646425e19e8682cf7c9055014cbdb54c6f +Author: Petr Gajdos +Date: Fri Mar 1 08:34:12 2013 +0100 + + Create rootdir entry volume label with mkdosfs, create it when + it doesn't exist with dosfslabel. + + See https://bugzilla.novell.com/show_bug.cgi?id=657011#c4 + for more information. + + Signed-off-by: Daniel Baumann + +commit 7963bda123aa3bc6e4525a69b8fb82c4e92f7a52 +Author: Petr Gajdos +Date: Fri Mar 1 08:33:18 2013 +0100 + + Forbid lowercase letters in label. + + See https://bugzilla.novell.com/show_bug.cgi?id=657011#c4 and + http://support.microsoft.com/kb/71715/en-us for more information. + + Signed-off-by: Daniel Baumann + +commit 23fd2911267f73ea8dd6b5e6cc7ffb60223063e5 +Author: Petr Gajdos +Date: Fri Mar 1 08:32:02 2013 +0100 + + Read label also from rootdir entry. + + See https://bugzilla.novell.com/show_bug.cgi?id=657011#c4 + for more information. + + Signed-off-by: Daniel Baumann + +commit dbf11935d610b3b8d50480fc6c6257f8d8508635 +Author: Petr Gajdos +Date: Fri Mar 1 08:30:21 2013 +0100 + + alloc_rootdir_entry() is intended to be called with pattern == "FSCK%04dREC", + the old code (probably c&p from auto_rename()) doesn't reflect this. + + Signed-off-by: Daniel Baumann + +commit 6de7543008fb5b37017560448c45270dafb62ab8 +Author: Petr Gajdos +Date: Fri Mar 1 08:29:00 2013 +0100 + + Instead of eleven blanks, fill in "NO NAME " as specification tells. + + See https://bugzilla.novell.com/show_bug.cgi?id=657011#c4 and + http://www.win.tue.nl/~aeb/linux/fs/fat/fat-1.html for more information. + + Signed-off-by: Daniel Baumann + +commit feb90914727acd92e4518881a07c3d80d6d6ccfa +Author: Daniel Baumann +Date: Fri Mar 1 08:58:15 2013 +0100 + + Write uppercase letters in label. + + See https://bugzilla.novell.com/show_bug.cgi?id=657011#c4 and + http://support.microsoft.com/kb/71715/en-us for more information. + + Signed-off-by: Daniel Baumann + +commit a75fb1c838a47b8f54ff567ca2eb71e98ccb691c +Author: Daniel Baumann +Date: Thu Feb 21 15:06:52 2013 +0100 + + Releasing version 3.0.15. + + Signed-off-by: Daniel Baumann + +commit c8f84fdcddf0982b86ef53250b08a005c80925ef +Author: Alexander Korolkov +Date: Mon Feb 4 00:22:34 2013 +0400 + + Using wcstombs() to convert LFN unicode characters to printable text. + + This closes Debian bug #596336. + + Signed-off-by: Daniel Baumann + +commit 154654555730874c1656bb6c63292a85a2260bdf +Author: Alexander Korolkov +Date: Sun Sep 5 18:59:47 2010 +0400 + + Recode short filenames from DOS codepage (default 437). + + Recode short filenames from DOS codepage (default 437) to the current + character encoding. This makes messages of dosfsck more readable. + Partially closes Debian bug #596336. + + Signed-off-by: Daniel Baumann + +commit ad76cae648d3b7895e97e16f8212ae8089b8846b +Author: Jaroslav Skarvada +Date: Thu Feb 21 14:40:52 2013 +0100 + + Fixing root directory allocation. + + See https://bugzilla.redhat.com/show_bug.cgi?id=674095 for more information. + + Signed-off-by: Daniel Baumann + +commit b8201b34eb83c2bf87bd1c2b1a7fa57a1dc28191 +Author: Jaroslav Skarvada +Date: Thu Feb 21 14:40:25 2013 +0100 + + Fixing device detection. + + See https://bugzilla.redhat.com/show_bug.cgi?id=710480 for more information. + + Signed-off-by: Daniel Baumann + +commit 7a756385ed6bce393396c699e068b5f239053db6 +Author: Daniel Baumann +Date: Wed Jan 23 13:22:01 2013 +0100 + + Releasing version 3.0.14. + + Signed-off-by: Daniel Baumann + +commit 5bdd7ef95c7f18e9e6571bf0718a564bd15b30d5 +Author: Daniel Baumann +Date: Wed Jan 23 13:16:20 2013 +0100 + + Documenting dosfsck -b in its manpage. + + Signed-off-by: Daniel Baumann + +commit a307be2b9cbfd0ed8c10dab79044cf6c7426ea18 +Author: Oleksij Rempel +Date: Wed Jan 23 12:36:56 2013 +0100 + + Adding option for bootsector read-only check. + + Most boot sectors may contains marker for filesystem state. We can this + bit on every mount and warn user if some thing wrong, without checking + complete filesystem. + + Signed-off-by: Oleksij Rempel + Signed-off-by: Daniel Baumann + +commit ce2f8dc349a8a6b2c26d1b3892cc9b5f0408bd34 +Author: Oleksij Rempel +Date: Wed Jan 23 12:35:13 2013 +0100 + + Checking boot sector for dirty bit. + + Some OSos use reseved byte of boot sector to set state of the file + system. If first bit set, then filesystem is proably damaged - write + operation was not finished/cache not snycted/... + + Signed-off-by: Oleksij Rempel + Signed-off-by: Daniel Baumann + +commit f33ee8ca5640191e7666402d6676073712b9c988 +Author: Daniel Baumann +Date: Wed Jan 23 12:25:59 2013 +0100 + + Completing and updating all copyright headers for 2013. + + Signed-off-by: Daniel Baumann + +commit bfe6d25c6297b2fbca174ead56dffee340668a06 +Author: Daniel Baumann +Date: Wed Jan 23 12:17:20 2013 +0100 + + Updating my email address. + + Signed-off-by: Daniel Baumann + +commit 13cdb4d262b4042ec2d94d706e30f45a63f1c029 +Author: Daniel Baumann +Date: Sat Jun 30 19:10:44 2012 +0200 + + Releasing version 3.0.13. + + Signed-off-by: Daniel Baumann + +commit d03948265679186dd89403da46d1aba1decafd55 +Author: Jaroslav Škarvada +Date: Sat Jun 30 19:09:11 2012 +0200 + + Fix 'dosfslabel throws "Seek to 114116076544:Invalid argument" error when labeling'. + + See https://bugzilla.redhat.com/show_bug.cgi?id=693662 for more information. + + Signed-off-by: Daniel Baumann + +commit e243612ccd10ec10a9877fe32524acc8b7f3d2a4 +Author: Daniel Baumann +Date: Sat Oct 29 08:40:53 2011 +0200 + + Releasing version 3.0.12. + + Signed-off-by: Daniel Baumann + +commit 025b4f04fe3148b68c1857e3a26292d6330c748b +Author: Michael Casadevall +Date: Tue Jun 7 19:19:30 2011 +0200 + + Correcting miscalculation of sector number in some cases. + + mkdosfs will incorrectly calculate the number of sectors of a + given FAT partition if the number sectors are odd due to + count_blocks incorrectly handling the remainder of a division + operation. This miscalculation causes the OMAP4 bootloader to + fail to boot. + + This bug can be observed by comparing the total sector size in + fdisk expert more to fsck.msdos; this discrepancy only shows up + when the number of sectors are odd. + + See https://bugs.launchpad.net/ubuntu/+source/dosfstools/+bug/794043 + for more information. + + Signed-off-by: Daniel Baumann + +commit 91a1fb9536b71133e6be9b0c09107c84d9cdfecb +Author: Daniel Baumann +Date: Sat Jan 8 23:38:59 2011 +0100 + + Re-running Nindent. + + Signed-off-by: Daniel Baumann + +commit 0390c4c1c12094aedb46d3bf800c9ac5900f9f56 +Author: Sergey Gusarov +Date: Sat Jan 8 23:36:11 2011 +0100 + + Fixing compiler warnings related to the mismatch of types "char *" / "unsigned + char *". + + These warnings appear when you compile the project with the option "-Wall", what + is done with the current default Makefile. + + Signed-off-by: Daniel Baumann + +commit 4a8f8a66459f6d311672c42f8a9ac3661fb79208 +Author: Jaroslav Skarvada +Date: Thu Jan 6 22:35:00 2011 +0100 + + Fixing overflow bug in reclaim_file function, see + https://bugzilla.redhat.com/show_bug.cgi?id=660154 for more information. + + The problem is that alloc_rootdir_entry counts with 10000 files at max, but the + filename buffer is only 8 chars long. Due to pattern mask used it results to + only 10 files at max (FSCK0-9REC). If there is more than 10 files, it overflows + and hangs. + + Signed-off-by: Daniel Baumann + +commit e0366dac9736a1a60459b33c2259785e8d08d647 +Author: Sergey Gusarov +Date: Thu Jan 6 22:31:39 2011 +0100 + + Fixing conversion specifiers in accordance with the type of expressions. + + Signed-off-by: Daniel Baumann + +commit 2d8be9c62869c220be6426ef9f37662b4e671454 +Author: Daniel Baumann +Date: Sun Jan 2 15:41:44 2011 +0100 + + Indenting source files. + + Signed-off-by: Daniel Baumann + +commit 697af859b69def125555403dc5521224c0813369 +Author: Daniel Baumann +Date: Sun Jan 2 15:39:03 2011 +0100 + + Adding Nindent script from syslinux. + + Signed-off-by: Daniel Baumann + +commit 66d55cd07420a7f6da3c96412b9159439823ff97 +Author: Daniel Baumann +Date: Fri Dec 24 17:58:29 2010 +0100 + + Releasing version 3.0.11. + + Signed-off-by: Daniel Baumann + +commit d57980204ef78b863130e9ef75f8e9f66bb677ec +Author: Michael Stapelberg +Date: Fri Nov 19 14:09:36 2010 +0100 + + Add better error message when the device cannot be opened. + + This is helpful for SD cards or other removable media which have an enabled + write lock -- without the "Permission denied" message, the user has to strace + mkdosfs to find out what's going on. + + Signed-off-by: Daniel Baumann + +commit bb6000fc30809ad8025bac5eb173d46f56223a7e +Author: Jaroslav Skarvada +Date: Fri Oct 8 13:38:16 2010 +0200 + + Unalign on s390x, see http://bugzilla.redhat.com/show_bug.cgi?id=624596 for + more information. + + Signed-off-by: Daniel Baumann + +commit 5ef7f1f78a13207fd4317a73dd0308c95faeaa44 +Author: Daniel Baumann +Date: Sun Sep 12 09:35:47 2010 +0200 + + Releasing version 3.0.10. + + Signed-off-by: Daniel Baumann + +commit ea41797c7a7a0f56774f326beb916815541e62ae +Author: Alexander Korolkov +Date: Sun Sep 12 09:29:12 2010 +0200 + + Modify LFN direntries when file is renamed or deleted, see + Debian bug #596329. + + Signed-off-by: Daniel Baumann + +commit e56ff72c1659a6a34c981d4ff6ce7841c773d0a8 +Author: Alexander Korolkov +Date: Sun Sep 12 09:27:07 2010 +0200 + + If the test of short filename fails, dosfsck could complain about + bad long filename, see Debian bug #596327. + + Signed-off-by: Daniel Baumann + +commit f0a42d0634302c3fbdbe4bb717b0a9a17235a828 +Author: Alexander Korolkov +Date: Sun Sep 12 09:24:47 2010 +0200 + + dosfsck: don't complain about bad filenames when short filename + contains 7 or more characters with codes 128-255, see Debian + bug #596327. + + Signed-off-by: Daniel Baumann + +commit 0113c5bd145749772e2c808224f7c571024eb5fa +Author: Mitch Rybczynski +Date: Mon Jul 5 14:45:54 2010 +0200 + + Adding __arm__ define check for some crosscompile toolchains. + + Signed-off-by: Daniel Baumann + +commit 88cb84f1402d23bdf94f3cfd934792e7a04333fc +Author: Daniel Baumann +Date: Sun Mar 14 16:42:32 2010 +0100 + + Modernizing dosfslabel manpage. + + Signed-off-by: Daniel Baumann + +commit 5aa7ec4f2d0e02207709eb0b4f1487ea9193dd26 +Author: Daniel Baumann +Date: Sun Mar 14 16:33:47 2010 +0100 + + Modernizing dosfsck manpage. + + Signed-off-by: Daniel Baumann + +commit 807ed8028357ebb5ec78d92c405c7c80c8f3d749 +Author: Daniel Baumann +Date: Sun Mar 14 16:05:32 2010 +0100 + + Fixing spelling error in boot.c. + + Signed-off-by: Daniel Baumann + +commit 5b6849dc6268dfdede6e57c50d28f4179416b127 +Author: Daniel Baumann +Date: Sun Jan 31 08:31:32 2010 +0100 + + Releasing version 3.0.9. + + Signed-off-by: Daniel Baumann + +commit 33bca7d6b5157ad7cde134f7edb3d6677317e15c +Author: Daniel Kahn Gillmor +Date: Sun Jan 31 00:11:41 2010 -0500 + + Be sure to store the updated reserved_sector count in the boot sector, + see Debian bug #567337. + + Signed-off-by: Daniel Baumann + +commit 2a3bef84fbee41ba055ecd57b6ded334e80b9b7f +Author: Daniel Baumann +Date: Sat Jan 23 10:16:18 2010 +0100 + + Releasing version 3.0.8. + + Signed-off-by: Daniel Baumann + +commit 726c02daf6f8d1f3bf47467bb3989357241ee24f +Author: Daniel Baumann +Date: Sat Jan 23 10:15:01 2010 +0100 + + Removing some cruft in end-comments. + + Signed-off-by: Daniel Baumann + +commit a5961d734410944ed88a7d67d6d27850e2ed065e +Author: Steven J. Magnani +Date: Thu Jan 21 16:58:11 2010 +0100 + + When compiling a 32-bit version of dosfstools on an x86_64 machine, + the resulting applications report strange errors on "large" (> 2 GiB) + partitions: + + Seek to -2118967808:Invalid argument + + Warning: Filesystem is FAT32 according to fat_length and fat32_length fields, + but has only 8613 clusters, less than the required minimum of 65525. + This may lead to problems on some systems. + + This appears to be due to compilation with a 32-bit off_t and lseek() library + function. + + Use lseek64 for positioning, and change some suspect uses of off_t to loff_t. + + Signed-off-by: Daniel Baumann + +commit bbb25bf11aeda92f6cc75d32a5fdb5789348bb08 +Author: Steven J. Magnani +Date: Thu Jan 21 16:56:26 2010 +0100 + + If dosfsck is run in read-only mode (-n), exit with code 0 + if the only issue found is an uninitialized free cluster summary. + + Signed-off-by: Daniel Baumann + +commit 1cae726e2da261e669557bb4a118371c8b836ad5 +Author: Steven J. Magnani +Date: Thu Jan 21 16:55:30 2010 +0100 + + On x86_64, dosfsck incorrectly claims that a free_cluster summary of + 0xFFFFFFFF, defined by Microsoft to be "uninitialized," is wrong. + + Signed-off-by: Daniel Baumann + +commit 62f806a2921a9ed0a44c3dd4d83a786b19924b72 +Author: H. Peter Anvin +Date: Fri Jan 8 09:16:38 2010 +0100 + + mkdosfs: correct alignment of the root directory. + + Correct the code to align the root directory; it was broken before + since bs.dir_entries had already been set at the point of alignment. + This patch removes the dual use of bs.dir_entries and root_dir_entries + to carry the same information: the information is carried in + root_dir_entires exclusively, and then bs.dir_entries is set inside + setup_tables() at a late point. + + The code to align the root directory is also wrapped in + if (align_structures); this avoids rounding the number of root + directory entries up to a whole sector when used with -a + (i.e. preserves the previous behavior.) + + Signed-off-by: Daniel Baumann + +commit 8825bda33bdce3ce30458dad26f7800b667516d3 +Author: H. Peter Anvin +Date: Wed Jan 6 20:55:36 2010 +0100 + + mkdosfs: improve wording in the man page for the -a option. + + Improve the English language used in the man page for the -a (no + align) option to mkdosfs. + + Signed-off-by: Daniel Baumann + +commit 21d3f8192aaa9958c626b87bcb9d94727b38ba80 +Author: Daniel Baumann +Date: Wed Jan 6 11:27:25 2010 +0100 + + Adding reference to dosfslable in mkdosfs manpage. + + Signed-off-by: Daniel Baumann + +commit 247ba068d4ceea886ca16c95ab4a8eb772dc4f7a +Author: H. Peter Anvin +Date: Wed Jan 6 11:18:55 2010 +0100 + + mkdosfs: by default align all structures to cluster boundaries + + Align all data structures (reserved sectors, FATs, root directory for + FAT12/16) to an even multiple of the cluster size. This means that if + the partition is aligned, so will all clusters be. This adds + significant performance for anything where the physical sector size is + larger than the logical sector size, e.g. flash media or large-sector + hard disks. + + Signed-off-by: Daniel Baumann + +commit 171bc07b0c3eff0eec01d899326ac2a34ea51e72 +Author: Daniel Baumann +Date: Thu Dec 24 10:53:36 2009 +0100 + + Releasing version 3.0.7. + + Signed-off-by: Daniel Baumann + +commit 28708fc8a66e571eb61d3fc2592c17b85f463b40 +Author: Ben Hutchings +Date: Thu Dec 24 09:55:52 2009 +0100 + + Fixing dosfslabel to set volume label in the right place, + see Debian bug #559985. + + Signed-off-by: Daniel Baumann + +commit 2c405dd8da0f7a1a006aaf91e548676a023bab92 +Author: Lubomir Rintel +Date: Thu Dec 24 09:39:39 2009 +0100 + + Fixing out-of bound writes. + + Firstly, packed attribute is added to the structure so that extension + is guarranteed to immediately follow name for the cross-name-extension + reads to succeed. + + Secondly, writes into dir_entry->name that span through the extension as + well are split into two, so that FORTIFY_SOURCE's bound checking does + not abort dosfsck. There also was an off-by-one error in auto_rename()'s + sprintf(). + + Signed-off-by: Daniel Baumann + +commit b8f3efed9cb0265968d0d5f2310eb9241a1b5650 +Author: San Mehat +Date: Thu Dec 24 09:31:41 2009 +0100 + + Adding custom exit code in dosfsck for the case where the FS is read only. + + Signed-off-by: Daniel Baumann + +commit 0657e018980f46932f7c438b4b1593c0ed10ccca +Author: Daniel Baumann +Date: Sun Oct 4 10:59:33 2009 +0200 + + Releasing version 3.0.6. + + Signed-off-by: Daniel Baumann + +commit bc842544573e4a6c4dcf14892ffd82aa7d590ae1 +Author: Steven J. Magnani +Date: Sun Oct 4 10:58:43 2009 +0200 + + Attempt to improve clarity of the orphan cluster reclaim code. + Minor optimization - remove some unnecessary checking. + + Signed-off-by: Daniel Baumann + +commit 8054b4a371a69eab1cb9c3c639dc38f5d3c03ddb +Author: Steven J. Magnani +Date: Sun Oct 4 08:37:19 2009 +0200 + + Close hole that permitted clusters to link to (invalid) cluster 1. + + If an orphan chain that linked to cluster 1 was reclaimed to a file, + deletion of the file would result in a filesystem panic. + + Signed-off-by: Daniel Baumann + +commit e51af88eed55631b35578c4a30f234ba6f829528 +Author: Steven J. Magnani +Date: Sun Oct 4 08:32:30 2009 +0200 + + Fix erroneous report of huge number of clusters in use on big-endian + systems when the FSINFO free cluster count is reset. + + Signed-off-by: Daniel Baumann + +commit 16ba63f98a310d9743e5b9dbd0f9d7a4f4717455 +Author: Daniel Baumann +Date: Mon Jul 27 14:26:11 2009 +0200 + + Releasing version 3.0.5. + + Signed-off-by: Daniel Baumann + +commit 28ff9d967c23caa8da10479c91d0ba8cc55397ea +Author: Piotr Kaczuba +Date: Sun Jul 26 22:21:25 2009 +0200 + + Signed/unsigned char mismatch in check.c causes false positives + in bad_name() and can result in data loss, see Debian bug #538758. + + Signed-off-by: Daniel Baumann + +commit d42a27373ac20030dac3849d532e79a5841fa957 +Author: Andrew Tridgell +Date: Sun Jul 26 22:12:06 2009 +0200 + + Update to new kernel patches that add FAT_NO_83NAME flag. + + See http://lkml.org/lkml/2009/7/20/425 and + http://lkml.org/lkml/2009/7/20/424 for more information. + + Signed-off-by: Daniel Baumann + +commit dd0f0b53926fbd3b0c262cc09b5d3e0fc19c7ec8 +Author: Daniel Baumann +Date: Tue Jul 21 08:10:52 2009 +0200 + + Releasing version 3.0.4. + + Signed-off-by: Daniel Baumann + +commit b9f37a61a63b8552806042328c1eb67fd4c8a5d2 +Author: Andrew Tridgell +Date: Tue Jul 21 07:59:22 2009 +0200 + + Modify dosfstools to support the dummy 8.3 short filename values + used by Linux systems with the VFAT_FS_DUALNAMES option disabled. + + See http://lkml.org/lkml/2009/6/26/313 and + http://lkml.org/lkml/2009/6/26/314 for more information. + + Signed-off-by: Daniel Baumann + +commit ecd15e86e8c1b9d24443e4fc1948c6effbcfaac3 +Author: Paul Rupe +Date: Tue May 19 10:37:52 2009 +0200 + + Fixing "Too many files need repair" error during fsck. + + Signed-off-by: Daniel Baumann + +commit 7c16098be2b04ab791a520bf4ca2319233f4bd73 +Author: Daniel Baumann +Date: Mon May 18 15:12:04 2009 +0200 + + Releasing version 3.0.3. + + Signed-off-by: Daniel Baumann + +commit b396dcfb09dba6c55575aaa4b94a408604febd27 +Author: Daniel Baumann +Date: Mon May 18 15:10:55 2009 +0200 + + Also declaring arm as an unaligned architecture, see Debian bug #502961. + + Signed-off-by: Daniel Baumann + +commit ff1b24e91db6df1076dc5c053f34ff1a3924578d +Author: Steven J. Magnani +Date: Mon May 18 15:01:49 2009 +0200 + + Adding support for limited-memory embedded systems. + + This patch reorganizes heap memory usage by dosfsck and mkdosfs + to support limited-memory embedded systems - in particular, those + based on Xilinx's Microblaze processor. It also adds a few comments. + + Signed-off-by: Daniel Baumann + +commit 89f0b727b56f544e40d8066d7711734fc57a051f +Author: Mike Frysinger +Date: Thu Mar 5 07:03:36 2009 +0100 + + Declaring Blackfin as an unaligned architecture. + + Signed-off-by: Daniel Baumann + +commit b54a8a46ef08e1993796673cfe6c732fe238f74d +Author: Daniel Baumann +Date: Sat Feb 28 09:48:04 2009 +0100 + + Releasing version 3.0.2. + + Signed-off-by: Daniel Baumann + +commit 95005294d8356b81401101e5f0473f49f45d05b7 +Author: Hiroaki Ishizawa +Date: Fri Feb 13 10:00:46 2009 +0100 + + dosfsck corrupts root directory when fs->nfats is 1. + + Signed-off-by: Daniel Baumann + +commit 043f8a8fb1decf9f6d4b6fcf23267264422a8dab +Author: Stepan Kasal +Date: Fri Jan 30 14:56:33 2009 +0100 + + src/dosfslabel.c (main): After writing the label, exit code should be 0. + + Signed-off-by: Daniel Baumann + +commit 017da27f1a2b7845cab5fb4e1259d3e0739ef777 +Author: Daniel Baumann +Date: Fri Jan 30 14:06:01 2009 +0100 + + Also installing ChangeLog in install-doc target of Makefile. + + Signed-off-by: Daniel Baumann + +commit 1c76f0faf837f203dc2b1353ce202111ca9183f0 +Author: Stepan Kasal +Date: Fri Jan 30 14:05:12 2009 +0100 + + Makefile: Do not clobber time stamps of doc files. + + Signed-off-by: Daniel Baumann + +commit df2d2f17898cb5e13e9017aef1ccaae3b11a201b +Author: Daniel Baumann +Date: Sun Nov 23 22:45:45 2008 +0100 + + Releasing version 3.0.1. + + Signed-off-by: Daniel Baumann + +commit 17b269b03d758b3946451e2724708d72a8882ed4 +Author: Daniel Baumann +Date: Sun Nov 23 18:41:01 2008 +0100 + + Applying Fedoras dosfstools-vfat-timingfix.diff from Bill Nottingham + to fix vfat timing issue. See + https://bugzilla.redhat.com/show_bug.cgi?id=448247 for more information. + + Signed-off-by: Daniel Baumann + +commit e597cafef44da9ca46eb2d4999a98c7e23072e46 +Author: Ulrich Mueller +Date: Tue Oct 7 07:55:37 2008 +0200 + + Patch to check for bad number of clusters in dosfsck: + + * FAT16 filesystems with 65525 clusters or more will be rejected + (Before, this was not tested for. Up to 65535 clusters were accepted + as good). + + * For FAT32 filesystems with less than 65525 a warning message will be + output. + + Macro MSDOS_FAT12 is now replaced by FAT12_THRESHOLD to make it + consistent with the definition in mkdosfs and to remove the dependency + on the kernel version. + + Signed-off-by: Daniel Baumann + +commit 90102bcd5447abbb7a155e25bb953a9e690c279c +Author: Dann Frazier +Date: Tue Sep 30 07:25:19 2008 +0200 + + Changing some wording to make the indended meaning of "full-disk device" + more obvious. + + Signed-off-by: Daniel Baumann + +commit 21e9ba0a43ed34e8424cac4fdb1d1d8d02e43336 +Author: Daniel Baumann +Date: Sun Sep 28 11:43:19 2008 +0200 + + Releasing version 3.0.0. + + Signed-off-by: Daniel Baumann + +commit eaf145d51bd47f34f0c1d36ea72242edae8660d5 +Author: Daniel Baumann +Date: Sun Sep 28 11:29:01 2008 +0200 + + Adding GPL headers to all files. + + Signed-off-by: Daniel Baumann + +commit 0826117103ea3e87f7b2cbd6f6daaa842f780b9f +Author: Daniel Baumann +Date: Sun Sep 28 10:51:55 2008 +0200 + + Adding new GPL license file. + + Signed-off-by: Daniel Baumann + +commit f8d6127d8ba4cd6c02ea698e14afa7f1bca59fb2 +Author: Daniel Baumann +Date: Fri Sep 26 23:31:12 2008 +0200 + + Redoing Makefile from scratch. + + Signed-off-by: Daniel Baumann + +commit b4feb7319f6c506f21346e1faea060e1d8429ccb +Author: Daniel Baumann +Date: Sat Sep 27 00:17:38 2008 +0200 + + Removing whitespaces in all files at EOL and EOF. + + Signed-off-by: Daniel Baumann + +commit 1410138d03618ac4a8f4a670bec1e6640c3dfe12 +Author: Daniel Baumann +Date: Fri Sep 26 23:48:56 2008 +0200 + + Adding Debians dosfslabel.8 manpage from Francois Wendling + . + + Signed-off-by: Daniel Baumann + +commit f62e7f27a7f7bcc51c30860ab0422deb273353bf +Author: Daniel Baumann +Date: Fri Sep 26 18:36:04 2008 +0200 + + Updating version.h includes to new location of version.h file. + + Signed-off-by: Daniel Baumann + +commit 32e5952f4f3610b5f28f427717d838c6e28307d6 +Author: Daniel Baumann +Date: Fri Sep 26 18:19:36 2008 +0200 + + Removing old lsm file. + + Signed-off-by: Daniel Baumann + +commit 25a433bd3d6d9f8bca75ea44afd101e8cf965308 +Author: Daniel Baumann +Date: Fri Sep 26 18:07:47 2008 +0200 + + Removing old cvsignore files. + + Signed-off-by: Daniel Baumann + +commit acac13fb85ed10120420be7a7bd15463389c9759 +Author: Daniel Baumann +Date: Fri Sep 26 18:18:39 2008 +0200 + + Removing old build file. + + Signed-off-by: Daniel Baumann + +commit 3ecdd2122504642e014916b2849e675dbdcddd73 +Author: Daniel Baumann +Date: Fri Sep 26 18:19:16 2008 +0200 + + Removing old GPL license files. + + Signed-off-by: Daniel Baumann + +commit f183d0eaa90668e70ce1e0f092baf66e88a213c9 +Author: Daniel Baumann +Date: Fri Sep 26 18:21:57 2008 +0200 + + Unifying dosfsck and mkdosfs Makefiles in common src/Makefile. + + Signed-off-by: Daniel Baumann + +commit 61e7466965387a881671c74156a14659362c3a4e +Author: Daniel Baumann +Date: Fri Sep 26 18:04:02 2008 +0200 + + Unifying dosfsck and mkdosfs sources in common src directory. + + Signed-off-by: Daniel Baumann + +commit 7552d5752706db94db069c40e844e6a01844cbae +Author: Daniel Baumann +Date: Fri Sep 26 18:05:27 2008 +0200 + + Unifying dosfsck and mkdosfs manpages in common man directory. + + Signed-off-by: Daniel Baumann + +commit 124598b2bdfc9901ce4a612f4ef4e4c916a2faa3 +Author: Daniel Baumann +Date: Fri Sep 26 18:12:29 2008 +0200 + + Unifying dosfsck and mkdosfs documents in common doc directory. + + Signed-off-by: Daniel Baumann + +commit fb9c46b59a1fdcea7360008abfe22136f0069d8b +Author: Daniel Baumann +Date: Fri Sep 26 15:39:51 2008 +0200 + + Applying Gentoos dosfstools-2.11-preen.patch from Roy Marples + to alias dosfsck -p to -a: + + * Map -p to -a for baselayout-2, #177514. + + Signed-off-by: Daniel Baumann + +commit aaa40a9bbc615e7947569e52724c2c5dae36577a +Author: Daniel Baumann +Date: Fri Sep 26 15:49:43 2008 +0200 + + Applying Gentoos dosfstools-2.11-build.patch from Mike Frysinger + to improve Makefile: + + * Respect user settings #157785/#157786 by Diego Petteno. + + Signed-off-by: Daniel Baumann + +commit 251626dd60a085675bf8120c047458312bb21f49 +Author: Daniel Baumann +Date: Fri Sep 26 15:37:34 2008 +0200 + + Applying Gentoos dosfstools-2.11-verify-double-count-fix.patch from + Robin H. Johnson to fix double count of files + during verification: + + * Don't double-count n_files during a verification pass. + Bugzilla: http://bugs.gentoo.org/show_bug.cgi?id=99845 + + Signed-off-by: Daniel Baumann + +commit e670ea82c9a0da446a0d7c88c580c4d2c8be7b59 +Author: Daniel Baumann +Date: Fri Sep 26 15:33:36 2008 +0200 + + Applying Gentoos dosfstools-2.11-fat32size.patch from Mike Frysinger + to fix generation of filesystems on 256meg devices: + + * Fix generation of FAT filesystems on devices that are 256meg in size + Patch by Ulrich Mueller and accepted upstream + http://bugs.gentoo.org/112504 + + Signed-off-by: Daniel Baumann + +commit a6dc6a4d4bb89ec3997a72fd084a83147c6e66b6 +Author: Daniel Baumann +Date: Fri Sep 26 15:22:06 2008 +0200 + + Applying Suses dosfstools-2.11-unsupported-sector-size.patch from Petr + Gajdos to add sector size warning: + + * added warning for creation msdos on filesystem with sector size + greater than 4096 [fate#303325] + + Signed-off-by: Daniel Baumann + +commit f74695618bd347cc42632794c278b208e464d6e9 +Author: Daniel Baumann +Date: Fri Sep 26 15:18:35 2008 +0200 + + Applying Suses dosfstools-2.11-mkdosfs-geo0.diff from Ludwig Nussel + to fix handling of zero heads and sectors: + + * the HDIO_GETGEO ioctl works on device mapper devices but returns + zero heads and sectors. Therefore let's a) assume dummy values in + that case in mkdosfs and b) don't consider such fat file systems as + invalid in dosfsck. The Linux kernel accepts them anyways. + + Signed-off-by: Daniel Baumann + +commit cf243e4a84f10db0f42ddadd663cb013c0f47e85 +Author: Daniel Baumann +Date: Fri Sep 26 15:15:40 2008 +0200 + + Applying Suses dosfstools-2.11-linuxfs.patch from Ruediger Oertel + to not include linux/fs.h. + + Signed-off-by: Daniel Baumann + +commit 2d4f18457e65556277fe203aeee2bc6e20f87a44 +Author: Daniel Baumann +Date: Fri Sep 26 15:11:50 2008 +0200 + + Applying Fedoras dosfstools-2.11-assumeKernel26.patch from Peter Vrabec + to remove linux 2.6 conditionals: + + * LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) does not work with + glibc-kernheaders-2.4-9.1.94 + + Signed-off-by: Daniel Baumann + +commit 739a6fb2880643a575c5b6d39444a2d0eca7badf +Author: Daniel Baumann +Date: Fri Sep 26 15:05:00 2008 +0200 + + Applying Debians 99-conglomeration.dpatch (no other information + available). + + Signed-off-by: Daniel Baumann + +commit 3b5ed8a4b186e8b5c1b72a20cbb07b36596550c8 +Author: Daniel Baumann +Date: Fri Sep 26 14:26:41 2008 +0200 + + Applying Debians 15-manpage-files.dpatch from Daniel Baumann + to improve dosfsck manpage: + + * Lists fsckNNNN.rec files in FILES section (Closes: #444596). + + Signed-off-by: Daniel Baumann + +commit 3b6a8634f4dd9500c16edff2b883ecac6cde8e88 +Author: Daniel Baumann +Date: Fri Sep 26 14:34:42 2008 +0200 + + Applying Debians 13-getopt.dpatch from Adonikam Virgo + to fix mkdosfs getopt: + + * Fixes backup sector getopt (Closes: #232387, #479794). + + Signed-off-by: Daniel Baumann + +commit 1b2c8ca3b4b1b308b684a032c794799c0d967536 +Author: Daniel Baumann +Date: Fri Sep 26 14:34:17 2008 +0200 + + Applying Debians 12-zero-slot.dpatch by Karl Tomlinson + to fix dosfsck zero slot crashes: + + * Fixes crashes due to zero slot numbers causing a negative offset in + the call to copy_lfn_part in lfn_add_slot. On amd64 this results in + a SIGSEGV in copy_lfn_part. On x86 the result is heap corruption and + thus sometimes a SIGSEGV or double free abort later. (Closes: + #152550, #353198, #356377, #401798). + + Signed-off-by: Daniel Baumann + +commit eec8585c731da25a24b731a500720a56d39df64e +Author: Daniel Baumann +Date: Fri Sep 26 14:33:54 2008 +0200 + + Applying Debians 11-memory-efficiency.dpatch from Eero Tamminen + to improve dosfsck memory efficiency: + + * Improves memory efficiency when checking filesystems. + + Signed-off-by: Daniel Baumann + +commit 06bd6697f3776156d646891fdef1cebb95c48ddc +Author: Daniel Baumann +Date: Fri Sep 26 14:33:28 2008 +0200 + + Applying Debians 10-manpage-synopsis.dpatch from Daniel Baumann + to fix manpage synopsis: + + * List alternative binary names in manpage synopsis (Closes: #284983). + + Signed-off-by: Daniel Baumann + +commit 42d340d05672d12a0cd001f89bfb508e05066f47 +Author: Daniel Baumann +Date: Fri Sep 26 14:32:46 2008 +0200 + + Applying Debians 09-manpage-fat32.dpatch from Daniel Baumann + to improve mkdosfs manpage: + + * Don't claim that FAT32 is not choosed automatically (Closes: + #414183). + + Signed-off-by: Daniel Baumann + +commit 0f5ce0d8f6bd5c7027e799a82f9fe6b814c0be6f +Author: Daniel Baumann +Date: Fri Sep 26 14:32:23 2008 +0200 + + Applying Debians 08-manpage-drop.dpatch from Daniel Baumann + to improve dosfsck manpage: + + * Don't use confusing word 'drop' when 'delete' is meant (Closes: + #134100). + + Signed-off-by: Daniel Baumann + +commit 8ec54ddfee9aa394a32bcaaa1945eb6273047d18 +Author: Daniel Baumann +Date: Fri Sep 26 14:31:50 2008 +0200 + + Applying Debians 07-manpage-spelling.dpatch from Justin Pryzby + to fix mkdosfs manpage typos. + + Signed-off-by: Daniel Baumann + +commit 4371588cfac03a516447e95164b19a6945efb7a7 +Author: Daniel Baumann +Date: Fri Sep 26 14:30:31 2008 +0200 + + Applying Suses dosfstools-2.11_determine-sector-size.patch from Petr + Gajdos to determine mkdosfs sector size automatically: + + * determine sector size of device automatically or if -S parameter + present, verify, that it's not under physical sector size + + Signed-off-by: Daniel Baumann + +commit fc92e197f870c2f161035fd0c90da5a3ae361be3 +Author: Daniel Baumann +Date: Fri Sep 26 14:30:03 2008 +0200 + + Applying Suses dosfstools-2.11-o_excl.patch from Pavol Rusnak + to use O_EXCL in mkdosfs: + + * mkdosfs now opens device with O_EXCL [#238687] + + Signed-off-by: Daniel Baumann + +commit 30846972f9d956b74ec1a3b6749798ab5191ce5a +Author: Daniel Baumann +Date: Fri Sep 26 14:29:36 2008 +0200 + + Applying Debians 04-unaligned-memory.dpatch from Khalid Aziz + to fix dosfsck unaligned memory accesses: + + * Fix unaligned memory accesses which cause warnings to appear + everytime the elilo bootloader script runs. This has led a number of + users to believe their install has failed (Closes: #258839). + + Signed-off-by: Daniel Baumann + +commit 6d5c09188c4549a1438656dd574b6142542174dd +Author: Daniel Baumann +Date: Fri Sep 26 13:47:40 2008 +0200 + + Applying Fedoras dosfstools-2.11-label.patch from Jeremy Katz + to add dosfslabel (originally by Peter Jones). + + Signed-off-by: Daniel Baumann + +commit 07ef48724795f7d415bd07850d2440c327b28541 +Author: Daniel Baumann +Date: Fri Sep 26 13:41:14 2008 +0200 + + Applying Fedoras dosfstools-2.11-fortify.patch from Jakub Jelinek + to make it build with -D_FORTIFY_SOURCE=2: + + * This violates -D_FORTIFY_SOURCE=2 (which is stricter than C + standard), but isn't actually any buffer overflow. But using memcpy + is more efficient anyway. + + Signed-off-by: Daniel Baumann + +commit 78f9dca40b2ba699daab0f7d8e7a2ff833919f94 +Author: Daniel Baumann +Date: Fri Sep 26 13:40:47 2008 +0200 + + Applying Fedoras dosfstools-2.7-argfix.patch (no other information + available). + + Signed-off-by: Daniel Baumann + +commit ba6774ae1dd5199a733dfaeaf438dff095284de7 +Author: Daniel Baumann +Date: Thu Jun 26 12:45:36 2008 +0200 + + Adding version 2.11. + + Signed-off-by: Daniel Baumann diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dea6661 --- /dev/null +++ b/Makefile @@ -0,0 +1,113 @@ +# Makefile +# +# Copyright (C) 2008-2013 Daniel Baumann +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# On Debian systems, the complete text of the GNU General Public License +# can be found in /usr/share/common-licenses/GPL-3 file. + + +DESTDIR = +PREFIX = /usr/local +SBINDIR = $(PREFIX)/sbin +DOCDIR = $(PREFIX)/share/doc +MANDIR = $(PREFIX)/share/man + +#OPTFLAGS = -O2 -fomit-frame-pointer -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 +OPTFLAGS = -O2 -fomit-frame-pointer $(shell getconf LFS_CFLAGS) +#WARNFLAGS = -Wall -pedantic -std=c99 +WARNFLAGS = -Wall +DEBUGFLAGS = -g +CFLAGS += $(OPTFLAGS) $(WARNFLAGS) $(DEBUGFLAGS) + +VPATH = src + +all: build + +build: dosfsck dosfslabel mkdosfs + +dosfsck: boot.o check.o common.o fat.o file.o io.o lfn.o charconv.o dosfsck.o + +dosfslabel: boot.o check.o common.o fat.o file.o io.o lfn.o charconv.o dosfslabel.o + +mkdosfs: mkdosfs.o + +rebuild: distclean build + +install: install-bin install-doc install-man + +install-bin: build + install -d -m 0755 $(DESTDIR)/$(SBINDIR) + install -m 0755 dosfsck dosfslabel mkdosfs $(DESTDIR)/$(SBINDIR) + + ln -sf dosfsck $(DESTDIR)/$(SBINDIR)/fsck.msdos + ln -sf dosfsck $(DESTDIR)/$(SBINDIR)/fsck.vfat + ln -sf mkdosfs $(DESTDIR)/$(SBINDIR)/mkfs.msdos + ln -sf mkdosfs $(DESTDIR)/$(SBINDIR)/mkfs.vfat + +install-doc: + install -d -m 0755 $(DESTDIR)/$(DOCDIR)/dosfstools + install -p -m 0644 ChangeLog doc/* $(DESTDIR)/$(DOCDIR)/dosfstools + +install-man: + install -d -m 0755 $(DESTDIR)/$(MANDIR)/man8 + install -p -m 0644 man/*.8 $(DESTDIR)/$(MANDIR)/man8 + + ln -sf dosfsck.8 $(DESTDIR)/$(MANDIR)/man8/fsck.msdos.8 + ln -sf dosfsck.8 $(DESTDIR)/$(MANDIR)/man8/fsck.vfat.8 + ln -sf mkdosfs.8 $(DESTDIR)/$(MANDIR)/man8/mkfs.msdos.8 + ln -sf mkdosfs.8 $(DESTDIR)/$(MANDIR)/man8/mkfs.vfat.8 + +uninstall: uninstall-bin uninstall-doc uninstall-man + +uninstall-bin: + rm -f $(DESTDIR)/$(SBINDIR)/dosfsck + rm -f $(DESTDIR)/$(SBINDIR)/dosfslabel + rm -f $(DESTDIR)/$(SBINDIR)/mkdosfs + + rm -f $(DESTDIR)/$(SBINDIR)/fsck.msdos + rm -f $(DESTDIR)/$(SBINDIR)/fsck.vfat + rm -f $(DESTDIR)/$(SBINDIR)/mkfs.msdos + rm -f $(DESTDIR)/$(SBINDIR)/mkfs.vfat + + rmdir --ignore-fail-on-non-empty $(DESTDIR)/$(SBINDIR) + +uninstall-doc: + rm -rf $(DESTDIR)/$(DOCDIR)/dosfstools + + rmdir --ignore-fail-on-non-empty $(DESTDIR)/$(DOCDIR) + +uninstall-man: + rm -f $(DESTDIR)/$(MANDIR)/man8/dosfsck.8 + rm -f $(DESTDIR)/$(MANDIR)/man8/dosfslabel.8 + rm -f $(DESTDIR)/$(MANDIR)/man8/mkdosfs.8 + + rm -f $(DESTDIR)/$(MANDIR)/man8/fsck.msdos.8 + rm -f $(DESTDIR)/$(MANDIR)/man8/fsck.vfat.8 + rm -f $(DESTDIR)/$(MANDIR)/man8/mkfs.msdos.8 + rm -f $(DESTDIR)/$(MANDIR)/man8/mkfs.vfat.8 + + rmdir --ignore-fail-on-non-empty $(DESTDIR)/$(MANDIR)/man8 + rmdir --ignore-fail-on-non-empty $(DESTDIR)/$(MANDIR) + +reinstall: distclean install + +clean: + rm -f *.o + +distclean: clean + rm -f dosfsck dosfslabel mkdosfs + +.PHONY: build rebuild install install-bin install-doc install-man uninstall uninstall-bin uninstall-doc uninstall-man reinstall clean distclean diff --git a/bin/Nindent b/bin/Nindent new file mode 100755 index 0000000..cf8ecfd --- /dev/null +++ b/bin/Nindent @@ -0,0 +1,18 @@ +#!/bin/sh +PARAM="-npro -kr -i4 -ts8 -sob -l80 -ss -ncs -cp1" +RES=`indent --version` +V1=`echo $RES | cut -d' ' -f3 | cut -d'.' -f1` +V2=`echo $RES | cut -d' ' -f3 | cut -d'.' -f2` +V3=`echo $RES | cut -d' ' -f3 | cut -d'.' -f3` +if [ $V1 -gt 2 ]; then + PARAM="$PARAM -il0" +elif [ $V1 -eq 2 ]; then + if [ $V2 -gt 2 ]; then + PARAM="$PARAM -il0"; + elif [ $V2 -eq 2 ]; then + if [ $V3 -ge 10 ]; then + PARAM="$PARAM -il0" + fi + fi +fi +exec indent $PARAM "$@" diff --git a/doc/ANNOUNCE.mkdosfs b/doc/ANNOUNCE.mkdosfs new file mode 100644 index 0000000..1f02ea2 --- /dev/null +++ b/doc/ANNOUNCE.mkdosfs @@ -0,0 +1,41 @@ +Announcing the release of mkdosfs version 0.3b (Yggdrasil) + +It seems I didn't get the bug completely fixed in 0.3a. Some +borderline cases would still allocate too many sectors for the FAT. +Again, nothing to worry about, just a nitpick -- this one would only +in certain cases add one sector per FAT. + +Announcing the release of mkdosfs version 0.3a (Yggdrasil) + +Fixed a bug which would cause too many sectors to be reserved for the +FAT (filesystem will still work fine, but have slightly less space +available). + +Announcing the release of mkdosfs version 0.3 (Yggdrasil) + +This version correctly handles even very large filesystems, and +properly supports the modern (3.3+) DOS bootsector format, including a +message printed on boot attempts. + +Peter Anvin +Yggdrasil Computing, Inc. +hpa@yggdrasil.com + + -------------- + +Announcing the release of mkdosfs version 0.2 + + +I've just uploaded mkdosfs to sunsite.unc.edu. It works in a similar way +to Remy Card's mke2fs, but creates an MS-DOS file system. + +The filename is mkdosfs-0.2.tar.gz. + +This second release should fix a small bug that could lead to FAT sizes that +Linux's dosfs would accept but MS-DOS wouldn't. + +The archive contains a manual page, binary and source versions. + + +Dave Hudson +dave@humbug.demon.co.uk diff --git a/doc/ChangeLog.dosfsck b/doc/ChangeLog.dosfsck new file mode 100644 index 0000000..f628ac2 --- /dev/null +++ b/doc/ChangeLog.dosfsck @@ -0,0 +1,10 @@ +Changes from version 0 to 1 +=========================== + + - fixed an off-by-two error in check.c:check_file + - fixed marking clusters bad in fat.c:set_fat + - fat.c:reclaim_free was also reclaiming bad clusters. + - fixed many incorrect byte sex conversions in check.c and fat.c + - -t and -w now require -a or -r + - added option -d to drop files. + - added option -u to try to "undelete" non-directory files. diff --git a/doc/ChangeLog.dosfstools-2.x b/doc/ChangeLog.dosfstools-2.x new file mode 100644 index 0000000..7ed9efe --- /dev/null +++ b/doc/ChangeLog.dosfstools-2.x @@ -0,0 +1,161 @@ +version 2.11 +============ + + - all: don't use own llseek() anymore, glibc lseek() does everything we need + - dosfsck: lfn.c: avoid segfault + - dosfsck: check.c, lfn.c: check for orphaned LFN slots + - dosfsck: check.c alloc_rootdir_entry(): set owner of newly alloced clusters + - dosfsck: dosfsck.h: better use for byte swapping + - dosfsck: io.c: added code for real DOS + - mkdosfs: raised FAT12_THRESHOLD from 4078 to 4085, introduced MIN_CLUST_32 + - mkdosfs: fix loop device size + - mkdosfs: by default, use FAT32 on devices >= 512MB + - mkdosfs: fix a memory leak (blank_sector) + - mkdosfs: fix parsing of number of blocks on command line, so that numbers + >2G can be used + - mkdosfs: add 'b' to getopt() string so this option can be used :) + - mkdosfs: fix parsing of -i arg (should be unsigned) + - mkdosfs: change default permissions of created images (-C) to 0666 & ~umask + - mkdosfs: relax geometry check: if HDIO_GETGEO fails, print a warning and + default to H=255,S=63 + - dosfsck: new option -n (no-op): just check non-interactively, but + don't write anything to filesystem + - A few #include changes to support compilation with linux 2.6 + headers (thanks to Jim Gifford ) + - dosfsck: remove directory entries pointing to start cluster 0, if they're + not "." or ".." entries that should actually point to the root dir + (pointed out by Thomas Winkler ) + - mkdosfs: new option -h to set number of hidden sectors + (thanks to Godwin Stewart ) + - all: updated my mail address everywhere... + +version 2.10 +============ + + - dosfsck: various 64-bit fixes and removed some warnings by Michal + Cihar + - mkdosfs: better error message if called without parameters (also + suggested by Michal) + +version 2.9 +=========== + + - dosfsck: if EOF from stdin, exit with error code + - dosfsck: Fix potential for "Internal error: next_cluster on bad cluster". + - dosfsck: When clearing long file names, don't overwrite the dir + entries with all zeros, but put 0xe5 into the first byte. + Otherwise, some OSes stop reading the directory at that point... + - dosfsck: in statistics printed by -v, fix 32bit overflow in number + of data bytes. + - dosfsck: fix an potential overflow in "too many clusters" check + - dosfsck: fix 64bit problem in fat.c (Debian bug #152769) + - dosfsck: allow FAT size > 32MB. + - dosfsck: allow for only one FAT + - dosfsck: with -v, also check that last sector of the filesystem can + be read (in case a partition is smaller than the fs thinks) + - mkdosfs: add note in manpage that creating bootable filesystems is + not supported. + - mkdosfs: better error message with pointer to -I if target is a + full-disk device. + +version 2.8 +=========== + + - dosfsck: Fixed endless loop whenever a volume label was present. + +version 2.7 +=========== + + - dosfsck: Don't check volume label for bad characters, everything + seems to be allowed there... Also ignore duplicate names where one of + them is a volume label. + +version 2.6 +=========== + + - mkdosfs: Added correct heads definition for 2.88M floppies if not + created via loopback. + - dosfsck: If boot sector and its backup are different (FAT32), offer + to write the backup to sector 0. (tnx to Pavel Roskin for this) + - For 64 bit alpha, struct bootsector in dosfsck.h must be defined + with __attribute__((packed)). + - mkdosfs now actually accepts -R option. (tnx to David Kerrawn) + - Fixed typo in dosfsck boot.c (recognition of boot signature in FSINFO) + - Various compilation fixes for 2.4 kernel headers and for ia64. + +version 2.5 +=========== + + - The llseek() implementation for alpha didn't really work; fixed it. + +version 2.4 +=========== + + - Fix compiling problem on alpha (made a silly typo...) + +version 2.3 +=========== + + - mkdosfs: Fixed usage message (printed only "bad address"). + - both: made man pages and usage statements more consistent. + - both: fix llseek function for alpha. + - dosfsck: fix reading of unaligned fields in boot sector for alpha. + - dosfsck: fixed renaming of files (extension wasn't really written). + +version 2.2 +=========== + + - Added dosfsck/COPYING, putting dosfsck officially under GPL (Werner + and I agree that it should be GPL). + - mkdosfs: Allow creation of a 16 bit FAT on filesystems that are too + small for it if the user explicitly selected FAT16 (but a warning + is printed). Formerly, you got the misleading error message "make + the fs a bit smaller". + - dosfsck: new option -y as synonym for -y; for compability with + other fs checkers, which also accept this option. + - dosfsck: Now prints messages similar to e2fsck: at start version + and feature list; at end number of files (and directories) and + number of used/total clusters. This makes the printouts of *fsck at + boot time nicer. + - dosfsck: -a (auto repair) now turns on -f (salvage files), too. -a + should act as non-destructive as possible, so lost clusters should + be assigned to files. Otherwise the data in them might be + overwritten later. + - dosfsck: Don't drop a directory with lots of bad entries in + auto-repair mode for the same reason as above. + - dosfsck: avoid deleting the whole FAT32 root dir if something is + wrong with it (bad start cluster or the like). + - general: also create symlinks {mkfs,fsck}.vfat.8 to the respective + real man pages. + +version 2.1 +=========== + + - Fix some forgotten loff_t's for filesystems > 4GB. (Thanks to + ). + - Fix typo in mkdosfs manpage. + - Removed inclusion of from mkdosfs.c; it's unnecessary and + caused problems in some environments. + - Fix condition when to expect . and .. entries in a directory. (Was + wrong for non-FAT32 if first entry in root dir was a directory also.) + - Also create mkfs.vfat and fsck.vfat symlinks, so that also + filesystems listed with type "vfat" in /etc/fstab can be + automatically checked. + +version 2.0 +=========== + + - merge of mkdosfs and dosfstools in one package + - new maintainer: Roman Hodek + - FAT32 support in both mkdosfs and dosfsck + - VFAT (long filename) support in dosfsck + - Support for Atari variant of MS-DOS filesystem in both tools + - Working support for big-endian systems in both tools + - Better support for loop devices in mkdosfs: usual floppy sizes are + detected and media byte etc. set accordingly; if loop fs has no + standard floppy size, use hd params + (mainly by Giuliano Procida ) + - Removed lots of gcc warnings + - Fixed some minor calculation bugs in mkdosfs. + +For change logs previous to 2.0, see the CHANGES files in the subdirectories. diff --git a/doc/ChangeLog.mkdosfs b/doc/ChangeLog.mkdosfs new file mode 100644 index 0000000..e39d9d6 --- /dev/null +++ b/doc/ChangeLog.mkdosfs @@ -0,0 +1,18 @@ +28th January 1995 H. Peter Anvin (hpa@yggdrasil.com) + + Better algorithm to select cluster sizes on large filesystems. + Added bogus boot sector code that on attempts to boot prints a + message (which can be chosen at mkdosfs time) and lets the user + press any key and try again. Corrected support for 1.2 Mb + floppies. mkdosfs now generates the extended bootsector + (superblock) format of DOS 3.3+, with support for volume ID's and + volume labels (volume labels are also written to the root + directory, as they should). + +18th February 1994 Dave Hudson (dave@humbug.demon.co.uk) + + Released version 0.2 - clears a bug in the FAT sizing code. + +1st September 1993 Dave Hudson (dave@humbug.demon.co.uk) + + Released version 0.1 - ALPHA release of mkdosfs diff --git a/doc/README.dosfsck b/doc/README.dosfsck new file mode 100644 index 0000000..7b351aa --- /dev/null +++ b/doc/README.dosfsck @@ -0,0 +1,60 @@ +dosfsck, version 1 +================== + +WARNING: This is ALPHA test software. Use at your own risk. + +dosfsck is the Linux equivalent of PC/MS-DOS' CHKDSK. It checks the +consistency of PC/MS-DOS file systems and optionally tries to repair +them. The tests dosfsck performs are described in the man page. + +dosfsck needs header files from dosfs.9 (or later) to compile. + +Before using dosfsck to repair a file system that contains data of any +value, you should verify that dosfsck is able to correct all reported +errors. (Except fatal errors and those reported as unfixable, of +course.) In order to do this, run it with the -V option, e.g. + + dosfsck -V /dev/sda1 (automatic check) +or dosfsck -V -r /dev/sda1 (interactive check and repair) + +dosfsck will perform two passes: in the first pass, inconsistencies are +detected and a list of changes to correct the problems is generated. In +the second pass, those changes are applied whenever dosfsck reads data +from disk. Hence no fixable errors should be reported in the second +pass if the first pass was successful. + +Please notify the author if fixable errors are reported in the second +pass. + +After verifying that dosfsck appears to be able to perform the desired +operations, either confirm that you want the changes to be performed +(if dosfsck was started with -r) or re-run dosfsck with the -a option +(if it was started without -r). + +Please send bug reports, comments, flames, etc. to +almesber@nessie.cs.id.ethz.ch or almesber@bernina.ethz.ch + +- Werner + +FAT32 and LFN support +===================== + +I've finally implemented some of the new features of MS-DOS +filesystems: FAT32 and long filenames. + +FAT32 is automatically detected and of course the different FAT +structure is handled. (Internally many changes were needed, so 32 bit +variables for all cluster numbers and 64 bit vars for offsets inside +the filesystem.) New checks for FAT32 are most notably on the backup +boot sector and the new info sector. Also the possibility that the +root directory resides in a cluster chain (instead of in a static +area) on FAT32 is handled. + +dosfscheck also knows about VFAT long filenames now. It parses those +names and uses them in listings etc. when available. There are also +some checks on the (cruel) structure of how LFNs are stored and some +attempts to fix problems. + +- Roman + +BTW, version 2 isn't ALPHA anymore :-) diff --git a/doc/README.dosfstools-2.x b/doc/README.dosfstools-2.x new file mode 100644 index 0000000..5fb00ed --- /dev/null +++ b/doc/README.dosfstools-2.x @@ -0,0 +1,60 @@ + +Atari format support +==================== + +Both mkdosfs and dosfsck now can also handle the Atari variation of +the MS-DOS filesystem format. The Atari format has some minor +differences, some caused by the different machine architecture (m68k), +some being "historic" (Atari didn't change some things that M$ +changed). + +Both tools automatically select Atari format if they run on an Atari. +Additionally the -A switch toggles between Atari and MS-DOS format. +I.e., on an Atari it selects plain DOS format, on any other machine it +switches to Atari format. + +The differences are in detail: + + - Atari TOS doesn't like cluster sizes != 2, so the usual solution + for bigger partitions was to increase the logical sector size. So + mkdosfs can handle sector sizes != 512 now, you can also manually + select it with the -S option. On filesystems larger than approx. 32 + MB, the sector size is automatically increased (stead of the + cluster size) to make the filesystem fit. mkdosfs will always use 2 + sectors per cluster (also with the floppy standard configurations), + except when directed otherwise on the command line. + + - From the docs, all values between 0xfff8 and 0xffff in the FAT mark + an end-of-file. However, DOS usually uses 0xfff8 and Atari 0xffff. + This seems to be only an consmetic difference. At least TOS doesn't + complain about 0xffff EOF marks. Don't know what DOS thinks of + 0xfff8 :-) Anyway, both tools use the EOF mark common to the + system (DOS/Atari). + + - Something similar of the bad cluster marks: On Atari the FAT values + 0xfff0 to 0xfff7 are used for this, under DOS only 0xfff7 (the + others can be normal cluster numbers, allowing 7 more clusters :-) + However, both systems usually mark with 0xfff7. Just dosfsck has to + interpret 0xfff0...0xfff7 differently. + + - Some fields in the boot sector are interpreted differently. For + example, Atari has a disk serial number (used to aid disk change + detection) where DOS stores the system name; the 'hidden' field is + 32 bit for DOS, but 16 bit for Atari, and there's no 'total_sect' + field; the 12/16 bit FAT decision is different: it's not based on + the number of clusters, but always FAT12 on floppies and FAT16 on + hard disks. mkdosfs nows about these differences and constructs the + boot sector accordingly. + + - In dosfsck, the boot sector differences also have to known, to not + warn about things that are no error on Atari. In addition, most + Atari formatting tools fill the 'tracks' and 'heads' fields with 0 + for hard disks, because they're meaningless on SCSI disks (Atari + has/had no IDE). Due to this, the check that they should be + non-zero is switched off. + + - Under Atari TOS, some other characters are illegal in filenames: + '<', '>', '|', '"', and ':' are allowed, but all non-ASCII chars + (codes >= 128) are forbidden. + +- Roman diff --git a/doc/README.mkdosfs b/doc/README.mkdosfs new file mode 100644 index 0000000..ae93ada --- /dev/null +++ b/doc/README.mkdosfs @@ -0,0 +1,50 @@ +mkdosfs - Make DOS file system utilty. + + +I wrote this, partially to complement the dosfsck utility written by Werner +Almesberger (who graciously gave me some pointers when I asked for some +advice about writing this code), and also to avoid me having to boot DOS +just to create data partitions (I use Linux to back up DOS :-) ). + +The code is really derived from Remy Card's mke2fs utility - I used this as a +framework, although all of the file system specific stuff was removed and the +DOS stuff inserted. I believe originally mke2fs was based on Linus' mkfs +code, hence the acknowledgements in the source code. + +Neither Remy nor Linus have had any involvement with mkdosfs, so if there are +any bugs they're almost certainly "all my own work". + +The code has been available for ftp since 1st September 1993, and I have yet +to receive any bug reports from users. I don't know of any bugs, but if you +do find a bug or have any constructive comments, please mail me! + +The only bug I found with version 0.1 was an obscure fault that could lead +to an invalid (for MS-DOS, not Linux's dos fs) number of sectors used in the +file allocation table(s). + + +Dave Hudson +dave@humbug.demon.co.uk + + +FAT32 support +============= + +mkdosfs now can also create filesystems in the new FAT32 format. To do +this, give mkdosfs a "-F 32" option. FAT32 isn't selected +automatically (yet), even if very large clusters are needed with +FAT16. With FAT32 you have two additional options, -R to select the +number of reserved sectors (usually 32), and -b to select the location +of the backup boot sector (default 6). Of course such a backup is +created, as well as the new info sector. On FAT32, the root directory +is always created as a cluster chain. Sorry, there's no switch to +generate an old static root dir. + +One bigger bug fix besides FAT32 was to reject filesystems that need a +16 bit FAT to fit all possible clusters, but the bigger FAT needs some +more sectors, so the total number of clusters drop below the border +where MS-DOS expects a 12 bit FAT. So such filesystems would be FAT16, +but interpreted as FAT32 by DOS. The fix is to reduce filesystem size +a bit. + +- Roman diff --git a/doc/TODO.dosfstools-2.x b/doc/TODO.dosfstools-2.x new file mode 100644 index 0000000..dbc2de0 --- /dev/null +++ b/doc/TODO.dosfstools-2.x @@ -0,0 +1,14 @@ + -*- mode: indented-text -*- + + - dosfsck: Better checking of file times: ctime <= mtime <= atime + + - mkdosfs: If /etc/bootsect.dos (or similar) exists, use it as a + template for generating boot sectors. This way, you can, e.g., make + bootable DOS disks. + + Addendum: Don't know if that's so wise... There are really many + variants of DOS/Windows bootcode out in the wild, and the code is + proprietary, too. + + - dosfsck: read-only sector test (-t without -a or -r); just print + out errors. diff --git a/man/dosfsck.8 b/man/dosfsck.8 new file mode 100644 index 0000000..50f5232 --- /dev/null +++ b/man/dosfsck.8 @@ -0,0 +1,135 @@ +.\" dosfsck.8 - manpage for dosfsck +.\" +.\" Copyright (C) 2006-2013 Daniel Baumann +.\" +.\" This program is free software: you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation, either version 3 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program. If not, see . +.\" +.\" On Debian systems, the complete text of the GNU General Public License +.\" can be found in /usr/share/common-licenses/GPL-3 file. +.\" +.\" +.TH DOSFSCK 8 "2010\-01\-31" "3.0.9" "check and repair MS\-DOS filesystems" + +.SH NAME +\fBdosfsck\fR \- check and repair MS\-DOS filesystems + +.SH SYNOPSIS +\fBdosfsck\fR|\fBfsck.msdos\fR|\fBfsck.vfat\fR [\-aAflnrtvVwy] [\-d \fIPATH\fR \-d\ \fI...\fR] [\-u\ \fIPATH\fR \-u \fI...\fR] \fIDEVICE\fR + +.SH DESCRIPTION +\fBdosfsck\fR verifies the consistency of MS\-DOS filesystems and optionally tries to repair them. +.PP +The following filesystem problems can be corrected (in this order): +.IP "*" 4 +FAT contains invalid cluster numbers. Cluster is changed to EOF. +.IP "*" 4 +File's cluster chain contains a loop. The loop is broken. +.IP "*" 4 +Bad clusters (read errors). The clusters are marked bad and they are removed from files owning them. This check is optional. +.IP "*" 4 +Directories with a large number of bad entries (probably corrupt). The directory can be deleted. +.IP "*" 4 +Files . and .. are non\-directories. They can be deleted or renamed. +.IP "*" 4 +Directories . and .. in root directory. They are deleted. +.IP "*" 4 +Bad filenames. They can be renamed. +.IP "*" 4 +Duplicate directory entries. They can be deleted or renamed. +.IP "*" 4 +Directories with non\-zero size field. Size is set to zero. +.IP "*" 4 +Directory . does not point to parent directory. The start pointer is adjusted. +.IP "*" 4 +Directory .. does not point to parent of parent directory. The start pointer is adjusted. +.IP "*" 4 +Start cluster number of a file is invalid. The file is truncated. +.IP "*" 4 +File contains bad or free clusters. The file is truncated. +.IP "*" 4 +File's cluster chain is longer than indicated by the size fields. The file is truncated. +.IP "*" 4 +Two or more files share the same cluster(s). All but one of the files are truncated. If the file being truncated is a directory file that has already been read, the filesystem check is restarted after truncation. +.IP "*" 4 +File's cluster chain is shorter than indicated by the size fields. The file is truncated. +.IP "*" 4 +Clusters are marked as used but are not owned by a file. They are marked as free. +.PP +Additionally, the following problems are detected, but not repaired: +.IP "*" 4 +Invalid parameters in boot sector. +.IP "*" 4 +Absence of . and .. entries in non\-root directories +.PP +When \fBdosfsck\fR checks a filesystem, it accumulates all changes in memory and performs them only after all checks are complete. This can be disabled with the \fB\-w\fR option. + +.SH OPTIONS +.IP "\fB\-a\fR" 4 +Automatically repair the filesystem. No user intervention is necessary. Whenever there is more than one method to solve a problem, the least destructive approach is used. +.IP "\fB\-A\fR" 4 +Use Atari variation of the MS\-DOS filesystem. This is default if \fBdosfsck\fR is run on an Atari, then this option turns off Atari format. There are some minor differences in Atari format: Some boot sector fields are interpreted slightly different, and the special FAT entries for end\-of\-file and bad cluster can be different. Under MS\-DOS 0xfff8 is used for EOF and Atari employs 0xffff by default, but both systems recognize all values from 0xfff8...0xffff as end\-of\-file. MS\-DOS uses only 0xfff7 for bad clusters, where on Atari values 0xfff0...0xfff7 are for this purpose (but the standard value is still 0xfff7). +.IP "\fB-b\fR" 4 +Make read-only boot sector check. +.IP "\fB\-d\fR" 4 +Delete the specified file. If more that one file with that name exists, the first one is deleted. +.IP "\fB\-f\fR" 4 +Salvage unused cluster chains to files. By default, unused clusters are added to the free disk space except in auto mode (\fB\-a\fR). +.IP "\fB\-l\fR" 4 +List path names of files being processed. +.IP "\fB\-n\fR" 4 +No\-operation mode: non\-interactively check for errors, but don't write +anything to the filesystem. +.IP "\fB\-r\fR" 4 +Interactively repair the filesystem. The user is asked for advice whenever +there is more than one approach to fix an inconsistency. +.IP "\fB\-t\fR" 4 +Mark unreadable clusters as bad. +.IP "\fB\-u\fR" 4 +Try to undelete the specified file. \fBdosfsck\fR tries to allocate a chain of contiguous unallocated clusters beginning with the start cluster of the undeleted file. +.IP "\fB\-v\fR" 4 +Verbose mode. Generates slightly more output. +.IP "\fB\-V\fR" 4 +Perform a verification pass. The filesystem check is repeated after the first run. The second pass should never report any fixable errors. It may take considerably longer than the first pass, because the first pass may have generated long list of modifications that have to be scanned for each disk read. +.IP "\fB\-w\fR" 4 +Write changes to disk immediately. +.IP "\fB\-y\fR" 4 +Same as \fB\-a\fR (automatically repair filesystem) for compatibility with other fsck tools. +.PP +\fBNote:\fR If \fB\-a\fR and \fB\-r\fR are absent, the filesystem is only checked, but not repaired. + +.SH "EXIT STATUS" +.IP "0" 4 +No recoverable errors have been detected. +.IP "1" 4 +Recoverable errors have been detected or \fBdosfsck\fR has discovered an internal inconsistency. +.IP "2" 4 +Usage error. \fBdosfsck\fR did not access the filesystem. + +.SH FILES +.IP "fsck0000.rec, fsck0001.rec, ..." 4 +When recovering from a corrupted filesystem, \fBdosfsck\fR dumps recovered data into files named 'fsckNNNN.rec' in the top level directory of the filesystem. + +.SH BUGS +Does not create . and .. files where necessary. Does not remove entirely empty directories. Should give more diagnostic messages. Undeleting files should use a more sophisticated algorithm. + +.SH SEE ALSO +\fBdosfslabel\fR(8) +.br +\fBmkdosfs\fR(8) + +.SH HOMEPAGE +More information about \fBdosfsck\fR and \fBdosfstools\fR can be found at <\fIhttp://www.daniel\-baumann.ch/software/dosfstools/\fR>. + +.SH AUTHORS +\fBdosfstools\fR were written by Werner Almesberger <\fIwerner.almesberger@lrc.di.epfl.ch\fR>, Roman Hodek <\fIRoman.Hodek@informatik.uni-erlangen.de\fR>, and others. The current maintainer is Daniel Baumann <\fImail@daniel-baumann.ch\fR>. diff --git a/man/dosfslabel.8 b/man/dosfslabel.8 new file mode 100644 index 0000000..e217ad6 --- /dev/null +++ b/man/dosfslabel.8 @@ -0,0 +1,50 @@ +.\" dosfslabel.8 - manpage for dosfsck +.\" +.\" Copyright (C) 2006-2013 Daniel Baumann +.\" +.\" This program is free software: you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation, either version 3 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program. If not, see . +.\" +.\" On Debian systems, the complete text of the GNU General Public License +.\" can be found in /usr/share/common-licenses/GPL-3 file. +.\" +.\" +.TH DOSFSLABEL 8 "2010\-01\-31" "3.0.9" "set or get MS\-DOS filesystem label" + +.SH NAME +\fBdosfslabel\fR \- set or get MS\-DOS filesystem label + +.SH SYNOPSIS +\fBdosfslabel\fR \fIDEVICE\fR [\fILABEL\fR] + +.SH DESCRIPTION +\fBdosfslabel\fR set or gets a MS\-DOS filesystem label from a given device. +.PP +If the label is omitted, then the label name of the specified device is written on the standard output. A label can't be longer than 11 characters. + +.SH OPTIONS +.IP "\fB\-h\fR, \fB\-\-help\fR" 4 +Displays a help message. +.IP "\fB\-V\fR, \fB\-\-version\fR" 4 +Shows version. + +.SH SEE ALSO +\fBdosfsck\fR(8) +.br +\fBmkdosfs\fR(8) + +.SH HOMEPAGE +More information about \fBdosfsck\fR and \fBdosfstools\fR can be found at <\fIhttp://www.daniel\-baumann.ch/software/dosfstools/\fR>. + +.SH AUTHORS +\fBdosfstools\fR were written by Werner Almesberger <\fIwerner.almesberger@lrc.di.epfl.ch\fR>, Roman Hodek <\fIRoman.Hodek@informatik.uni-erlangen.de\fR>, and others. The current maintainer is Daniel Baumann <\fImail@daniel-baumann.ch\fR>. diff --git a/man/mkdosfs.8 b/man/mkdosfs.8 new file mode 100644 index 0000000..840ac41 --- /dev/null +++ b/man/mkdosfs.8 @@ -0,0 +1,244 @@ +.\" mkdosfs.8 - manpage for dosfsck +.\" +.\" Copyright (C) 2006-2013 Daniel Baumann +.\" +.\" This program is free software: you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation, either version 3 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program. If not, see . +.\" +.\" On Debian systems, the complete text of the GNU General Public License +.\" can be found in /usr/share/common-licenses/GPL-3 file. +.\" +.\" +.TH MKDOSFS 8 "5 May 1995" "Version 2.x" +.SH NAME +.B mkdosfs +\- create an MS-DOS file system under Linux +.SH SYNOPSIS +.B mkdosfs|mkfs.msdos|mkfs.vfat +[ +.B \-a +] +[ +.B \-A +] +[ +.B \-b +.I sector-of-backup +] +[ +.B \-c +] +[ +.B \-l +.I filename +] +[ +.B \-C +] +[ +.B \-f +.I number-of-FATs +] +[ +.B \-F +.I FAT-size +] +[ +.B \-h +.I number-of-hidden-sectors +] +[ +.B \-i +.I volume-id +] +.RB [ " \-I " ] +[ +.B \-m +.I message-file +] +[ +.B \-n +.I volume-name +] +[ +.B \-r +.I root-dir-entries +] +[ +.B \-R +.I number-of-reserved-sectors +] +[ +.B \-s +.I sectors-per-cluster +] +[ +.B \-S +.I logical-sector-size +] +[ +.B \-v +] +.I device +[ +.I block-count +] +.SH DESCRIPTION +.B mkdosfs +is used to create an MS-DOS file system under Linux on a device (usually +a disk partition). +.I device +is the special file corresponding to the device (e.g /dev/hdXX). +.I block-count +is the number of blocks on the device. If omitted, +.B mkdosfs +automatically determines the file system size. +.SH OPTIONS +.TP +.B \-a +Normally, for any filesystem except very small ones, \fBmkdosfs\fP +will align all the data structures to cluster size, to make sure that +as long as the partition is properly aligned, so will all the data +structures in the filesystem. This option disables alignment; this +may provide a handful of additional clusters of storage at the expense +of a significant performance degradation on RAIDs, flash media or +large-sector hard disks. +.TP +.B \-A +Use Atari variation of the MS-DOS file system. This is default if +\fBmkdosfs\fP is run on an Atari, then this option turns off Atari +format. There are some differences when using Atari format: If not +directed otherwise by the user, \fBmkdosfs\fP will always use 2 +sectors per cluster, since GEMDOS doesn't like other values very much. +It will also obey the maximum number of sectors GEMDOS can handle. +Larger file systems are managed by raising the logical sector size. +Under Atari format, an Atari-compatible serial number for the +file system is generated, and a 12 bit FAT is used only for file systems +that have one of the usual floppy sizes (720k, 1.2M, 1.44M, 2.88M), a +16 bit FAT otherwise. This can be overridden with the \fB\-F\fP +option. Some PC-specific boot sector fields aren't written, and a boot +message (option \fB\-m\fP) is ignored. +.TP +.BI \-b " sector-of-backup " +Selects the location of the backup boot sector for FAT32. Default +depends on number of reserved sectors, but usually is sector 6. The +backup must be within the range of reserved sectors. +.TP +.B \-c +Check the device for bad blocks before creating the file system. +.TP +.B \-C +Create the file given as \fIdevice\fP on the command line, and write +the to-be-created file system to it. This can be used to create the +new file system in a file instead of on a real device, and to avoid +using \fBdd\fP in advance to create a file of appropriate size. With +this option, the \fIblock-count\fP must be given, because otherwise +the intended size of the file system wouldn't be known. The file +created is a sparse file, which actually only contains the meta-data +areas (boot sector, FATs, and root directory). The data portions won't +be stored on the disk, but the file nevertheless will have the +correct size. The resulting file can be copied later to a floppy disk +or other device, or mounted through a loop device. +.TP +.BI \-f " number-of-FATs" +Specify the number of file allocation tables in the file system. The +default is 2. Currently the Linux MS-DOS file system does not support +more than 2 FATs. +.TP +.BI \-F " FAT-size" +Specifies the type of file allocation tables used (12, 16 or 32 bit). +If nothing is specified, \fBmkdosfs\fR will automatically select +between 12, 16 and 32 bit, whatever fits better for the file system size. +.TP +.BI \-h " number-of-hidden-sectors " +Select the number of hidden sectors in the volume. Apparently some +digital cameras get indigestion if you feed them a CF card without +such hidden sectors, this option allows you to satisfy them. Assumes +\'0\' if no value is given on the command line. +.TP +.I \-i " volume-id" +Sets the volume ID of the newly created file system; +.I volume-id +is a 32-bit hexadecimal number (for example, 2e24ec82). The default +is a number which depends on the file system creation time. +.TP +.B \-I +It is typical for fixed disk devices to be partitioned so, by default, you are +not permitted to create a filesystem across the entire device. +.B mkdosfs +will complain and tell you that it refuses to work. This is different +when using MO disks. One doesn't always need partitions on MO disks. +The file system can go directly to the whole disk. Under other OSes +this is known as the 'superfloppy' format. + +This switch will force +.B mkdosfs +to work properly. +.TP +.BI \-l " filename" +Read the bad blocks list from +.IR filename . +.TP +.BI \-m " message-file" +Sets the message the user receives on attempts to boot this file system +without having properly installed an operating system. The message +file must not exceed 418 bytes once line feeds have been converted to +carriage return-line feed combinations, and tabs have been expanded. +If the filename is a hyphen (-), the text is taken from standard input. +.TP +.BI \-n " volume-name" +Sets the volume name (label) of the file system. The volume name can +be up to 11 characters long. The default is no label. +.TP +.BI \-r " root-dir-entries" +Select the number of entries available in the root directory. The +default is 112 or 224 for floppies and 512 for hard disks. +.TP +.BI \-R " number-of-reserved-sectors " +Select the number of reserved sectors. With FAT32 format at least 2 +reserved sectors are needed, the default is 32. Otherwise the default +is 1 (only the boot sector). +.TP +.BI \-s " sectors-per-cluster" +Specify the number of disk sectors per cluster. Must be a power of 2, +i.e. 1, 2, 4, 8, ... 128. +.TP +.BI \-S " logical-sector-size" +Specify the number of bytes per logical sector. Must be a power of 2 +and greater than or equal to 512, i.e. 512, 1024, 2048, 4096, 8192, +16384, or 32768. +.TP +.B \-v +Verbose execution. +.SH BUGS +.B mkdosfs +can not create boot-able file systems. This isn't as easy as you might +think at first glance for various reasons and has been discussed a lot +already. +.B mkdosfs +simply will not support it ;) +.SH AUTHOR +Dave Hudson - ; modified by Peter Anvin +. Fixes and additions by Roman Hodek + for Debian/GNU Linux. +.SH ACKNOWLEDGMENTS +.B mkdosfs +is based on code from +.BR mke2fs +(written by Remy Card - ) which is itself based on +.BR mkfs +(written by Linus Torvalds - ). +.SH SEE ALSO +.BR dosfsck (8), +.BR dosfslabel (8), +.BR mkfs (8) diff --git a/src/boot.c b/src/boot.c new file mode 100644 index 0000000..87c98f7 --- /dev/null +++ b/src/boot.c @@ -0,0 +1,577 @@ +/* boot.c - Read and analyze ia PC/MS-DOS boot sector + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek */ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "dosfsck.h" +#include "fat.h" +#include "io.h" +#include "boot.h" +#include "check.h" + +#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0) + /* don't divide by zero */ + +/* cut-over cluster counts for FAT12 and FAT16 */ +#define FAT12_THRESHOLD 4085 +#define FAT16_THRESHOLD 65525 + +static struct { + __u8 media; + char *descr; +} mediabytes[] = { + { + 0xf0, "5.25\" or 3.5\" HD floppy"}, { + 0xf8, "hard disk"}, { + 0xf9, "3,5\" 720k floppy 2s/80tr/9sec or " + "5.25\" 1.2M floppy 2s/80tr/15sec"}, { + 0xfa, "5.25\" 320k floppy 1s/80tr/8sec"}, { + 0xfb, "3.5\" 640k floppy 2s/80tr/8sec"}, { + 0xfc, "5.25\" 180k floppy 1s/40tr/9sec"}, { + 0xfd, "5.25\" 360k floppy 2s/40tr/9sec"}, { + 0xfe, "5.25\" 160k floppy 1s/40tr/8sec"}, { +0xff, "5.25\" 320k floppy 2s/40tr/8sec"},}; + +#if defined __alpha || defined __arm || defined __arm__ || defined __ia64__ || defined __x86_64__ \ + || defined __ppc64__ || defined __bfin__ || defined __MICROBLAZE__ +/* Unaligned fields must first be copied byte-wise */ +#define GET_UNALIGNED_W(f) \ + ({ \ + unsigned short __v; \ + memcpy( &__v, &f, sizeof(__v) ); \ + CF_LE_W( *(unsigned short *)&__v ); \ + }) +#else +#define GET_UNALIGNED_W(f) CF_LE_W( *(unsigned short *)&f ) +#endif + +static char *get_media_descr(unsigned char media) +{ + int i; + + for (i = 0; i < sizeof(mediabytes) / sizeof(*mediabytes); ++i) { + if (mediabytes[i].media == media) + return (mediabytes[i].descr); + } + return ("undefined"); +} + +static void dump_boot(DOS_FS * fs, struct boot_sector *b, unsigned lss) +{ + unsigned short sectors; + + printf("Boot sector contents:\n"); + if (!atari_format) { + char id[9]; + strncpy(id, (const char *)b->system_id, 8); + id[8] = 0; + printf("System ID \"%s\"\n", id); + } else { + /* On Atari, a 24 bit serial number is stored at offset 8 of the boot + * sector */ + printf("Serial number 0x%x\n", + b->system_id[5] | (b-> + system_id[6] << 8) | (b->system_id[7] << 16)); + } + printf("Media byte 0x%02x (%s)\n", b->media, get_media_descr(b->media)); + printf("%10d bytes per logical sector\n", GET_UNALIGNED_W(b->sector_size)); + printf("%10d bytes per cluster\n", fs->cluster_size); + printf("%10d reserved sector%s\n", CF_LE_W(b->reserved), + CF_LE_W(b->reserved) == 1 ? "" : "s"); + printf("First FAT starts at byte %llu (sector %llu)\n", + (unsigned long long)fs->fat_start, + (unsigned long long)fs->fat_start / lss); + printf("%10d FATs, %d bit entries\n", b->fats, fs->fat_bits); + printf("%10d bytes per FAT (= %u sectors)\n", fs->fat_size, + fs->fat_size / lss); + if (!fs->root_cluster) { + printf("Root directory starts at byte %llu (sector %llu)\n", + (unsigned long long)fs->root_start, + (unsigned long long)fs->root_start / lss); + printf("%10d root directory entries\n", fs->root_entries); + } else { + printf("Root directory start at cluster %lu (arbitrary size)\n", + fs->root_cluster); + } + printf("Data area starts at byte %llu (sector %llu)\n", + (unsigned long long)fs->data_start, + (unsigned long long)fs->data_start / lss); + printf("%10lu data clusters (%llu bytes)\n", fs->clusters, + (unsigned long long)fs->clusters * fs->cluster_size); + printf("%u sectors/track, %u heads\n", CF_LE_W(b->secs_track), + CF_LE_W(b->heads)); + printf("%10u hidden sectors\n", atari_format ? + /* On Atari, the hidden field is only 16 bit wide and unused */ + (((unsigned char *)&b->hidden)[0] | + ((unsigned char *)&b->hidden)[1] << 8) : CF_LE_L(b->hidden)); + sectors = GET_UNALIGNED_W(b->sectors); + printf("%10u sectors total\n", sectors ? sectors : CF_LE_L(b->total_sect)); +} + +static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, int lss) +{ + struct boot_sector b2; + + if (!fs->backupboot_start) { + printf("There is no backup boot sector.\n"); + if (CF_LE_W(b->reserved) < 3) { + printf("And there is no space for creating one!\n"); + return; + } + if (interactive) + printf("1) Create one\n2) Do without a backup\n"); + else + printf(" Auto-creating backup boot block.\n"); + if (!interactive || get_key("12", "?") == '1') { + int bbs; + /* The usual place for the backup boot sector is sector 6. Choose + * that or the last reserved sector. */ + if (CF_LE_W(b->reserved) >= 7 && CF_LE_W(b->info_sector) != 6) + bbs = 6; + else { + bbs = CF_LE_W(b->reserved) - 1; + if (bbs == CF_LE_W(b->info_sector)) + --bbs; /* this is never 0, as we checked reserved >= 3! */ + } + fs->backupboot_start = bbs * lss; + b->backup_boot = CT_LE_W(bbs); + fs_write(fs->backupboot_start, sizeof(*b), b); + fs_write((loff_t) offsetof(struct boot_sector, backup_boot), + sizeof(b->backup_boot), &b->backup_boot); + printf("Created backup of boot sector in sector %d\n", bbs); + return; + } else + return; + } + + fs_read(fs->backupboot_start, sizeof(b2), &b2); + if (memcmp(b, &b2, sizeof(b2)) != 0) { + /* there are any differences */ + __u8 *p, *q; + int i, pos, first = 1; + char buf[20]; + + printf("There are differences between boot sector and its backup.\n"); + printf("Differences: (offset:original/backup)\n "); + pos = 2; + for (p = (__u8 *) b, q = (__u8 *) & b2, i = 0; i < sizeof(b2); + ++p, ++q, ++i) { + if (*p != *q) { + sprintf(buf, "%s%u:%02x/%02x", first ? "" : ", ", + (unsigned)(p - (__u8 *) b), *p, *q); + if (pos + strlen(buf) > 78) + printf("\n "), pos = 2; + printf("%s", buf); + pos += strlen(buf); + first = 0; + } + } + printf("\n"); + + if (interactive) + printf("1) Copy original to backup\n" + "2) Copy backup to original\n" "3) No action\n"); + else + printf(" Not automatically fixing this.\n"); + switch (interactive ? get_key("123", "?") : '3') { + case '1': + fs_write(fs->backupboot_start, sizeof(*b), b); + break; + case '2': + fs_write(0, sizeof(b2), &b2); + break; + default: + break; + } + } +} + +static void init_fsinfo(struct info_sector *i) +{ + i->magic = CT_LE_L(0x41615252); + i->signature = CT_LE_L(0x61417272); + i->free_clusters = CT_LE_L(-1); + i->next_cluster = CT_LE_L(2); + i->boot_sign = CT_LE_W(0xaa55); +} + +static void read_fsinfo(DOS_FS * fs, struct boot_sector *b, int lss) +{ + struct info_sector i; + + if (!b->info_sector) { + printf("No FSINFO sector\n"); + if (interactive) + printf("1) Create one\n2) Do without FSINFO\n"); + else + printf(" Not automatically creating it.\n"); + if (interactive && get_key("12", "?") == '1') { + /* search for a free reserved sector (not boot sector and not + * backup boot sector) */ + __u32 s; + for (s = 1; s < CF_LE_W(b->reserved); ++s) + if (s != CF_LE_W(b->backup_boot)) + break; + if (s > 0 && s < CF_LE_W(b->reserved)) { + init_fsinfo(&i); + fs_write((loff_t) s * lss, sizeof(i), &i); + b->info_sector = CT_LE_W(s); + fs_write((loff_t) offsetof(struct boot_sector, info_sector), + sizeof(b->info_sector), &b->info_sector); + if (fs->backupboot_start) + fs_write(fs->backupboot_start + + offsetof(struct boot_sector, info_sector), + sizeof(b->info_sector), &b->info_sector); + } else { + printf("No free reserved sector found -- " + "no space for FSINFO sector!\n"); + return; + } + } else + return; + } + + fs->fsinfo_start = CF_LE_W(b->info_sector) * lss; + fs_read(fs->fsinfo_start, sizeof(i), &i); + + if (i.magic != CT_LE_L(0x41615252) || + i.signature != CT_LE_L(0x61417272) || i.boot_sign != CT_LE_W(0xaa55)) { + printf("FSINFO sector has bad magic number(s):\n"); + if (i.magic != CT_LE_L(0x41615252)) + printf(" Offset %llu: 0x%08x != expected 0x%08x\n", + (unsigned long long)offsetof(struct info_sector, magic), + CF_LE_L(i.magic), 0x41615252); + if (i.signature != CT_LE_L(0x61417272)) + printf(" Offset %llu: 0x%08x != expected 0x%08x\n", + (unsigned long long)offsetof(struct info_sector, signature), + CF_LE_L(i.signature), 0x61417272); + if (i.boot_sign != CT_LE_W(0xaa55)) + printf(" Offset %llu: 0x%04x != expected 0x%04x\n", + (unsigned long long)offsetof(struct info_sector, boot_sign), + CF_LE_W(i.boot_sign), 0xaa55); + if (interactive) + printf("1) Correct\n2) Don't correct (FSINFO invalid then)\n"); + else + printf(" Auto-correcting it.\n"); + if (!interactive || get_key("12", "?") == '1') { + init_fsinfo(&i); + fs_write(fs->fsinfo_start, sizeof(i), &i); + } else + fs->fsinfo_start = 0; + } + + if (fs->fsinfo_start) + fs->free_clusters = CF_LE_L(i.free_clusters); +} + +static char print_fat_dirty_state(void) +{ + printf("Dirty bit is set. Fs was not properly unmounted and" + " some data may be corrupt.\n"); + + if (interactive) { + printf("1) Remove dirty bit\n" + "2) No action\n"); + return get_key("12", "?"); + } else + printf(" Automaticaly removing dirty bit.\n"); + return '1'; +} + +static void check_fat_state_bit(DOS_FS *fs, void *b) +{ + if (fs->fat_bits == 32) { + struct boot_sector *b32 = b; + + if (b32->reserved3 & FAT_STATE_DIRTY) { + printf("0x41: "); + if (print_fat_dirty_state() == '1') { + b32->reserved3 &= ~FAT_STATE_DIRTY; + fs_write(0, sizeof(*b32), b32); + } + } + } else { + struct boot_sector_16 *b16 = b; + + if (b16->reserved2 & FAT_STATE_DIRTY) { + printf("0x25: "); + if (print_fat_dirty_state() == '1') { + b16->reserved2 &= ~FAT_STATE_DIRTY; + fs_write(0, sizeof(*b16), b16); + } + } + } +} + +void read_boot(DOS_FS * fs) +{ + struct boot_sector b; + unsigned total_sectors; + unsigned short logical_sector_size, sectors; + unsigned fat_length; + loff_t data_size; + + fs_read(0, sizeof(b), &b); + logical_sector_size = GET_UNALIGNED_W(b.sector_size); + if (!logical_sector_size) + die("Logical sector size is zero."); + + /* This was moved up because it's the first thing that will fail */ + /* if the platform needs special handling of unaligned multibyte accesses */ + /* but such handling isn't being provided. See GET_UNALIGNED_W() above. */ + if (logical_sector_size & (SECTOR_SIZE - 1)) + die("Logical sector size (%d bytes) is not a multiple of the physical " + "sector size.", logical_sector_size); + + fs->cluster_size = b.cluster_size * logical_sector_size; + if (!fs->cluster_size) + die("Cluster size is zero."); + if (b.fats != 2 && b.fats != 1) + die("Currently, only 1 or 2 FATs are supported, not %d.\n", b.fats); + fs->nfats = b.fats; + sectors = GET_UNALIGNED_W(b.sectors); + total_sectors = sectors ? sectors : CF_LE_L(b.total_sect); + if (verbose) + printf("Checking we can access the last sector of the filesystem\n"); + /* Can't access last odd sector anyway, so round down */ + fs_test((loff_t) ((total_sectors & ~1) - 1) * (loff_t) logical_sector_size, + logical_sector_size); + fat_length = CF_LE_W(b.fat_length) ? + CF_LE_W(b.fat_length) : CF_LE_L(b.fat32_length); + fs->fat_start = (loff_t) CF_LE_W(b.reserved) * logical_sector_size; + fs->root_start = ((loff_t) CF_LE_W(b.reserved) + b.fats * fat_length) * + logical_sector_size; + fs->root_entries = GET_UNALIGNED_W(b.dir_entries); + fs->data_start = fs->root_start + ROUND_TO_MULTIPLE(fs->root_entries << + MSDOS_DIR_BITS, + logical_sector_size); + data_size = (loff_t) total_sectors *logical_sector_size - fs->data_start; + fs->clusters = data_size / fs->cluster_size; + fs->root_cluster = 0; /* indicates standard, pre-FAT32 root dir */ + fs->fsinfo_start = 0; /* no FSINFO structure */ + fs->free_clusters = -1; /* unknown */ + if (!b.fat_length && b.fat32_length) { + fs->fat_bits = 32; + fs->root_cluster = CF_LE_L(b.root_cluster); + if (!fs->root_cluster && fs->root_entries) + /* M$ hasn't specified this, but it looks reasonable: If + * root_cluster is 0 but there is a separate root dir + * (root_entries != 0), we handle the root dir the old way. Give a + * warning, but convertig to a root dir in a cluster chain seems + * to complex for now... */ + printf("Warning: FAT32 root dir not in cluster chain! " + "Compatibility mode...\n"); + else if (!fs->root_cluster && !fs->root_entries) + die("No root directory!"); + else if (fs->root_cluster && fs->root_entries) + printf("Warning: FAT32 root dir is in a cluster chain, but " + "a separate root dir\n" + " area is defined. Cannot fix this easily.\n"); + if (fs->clusters < FAT16_THRESHOLD) + printf("Warning: Filesystem is FAT32 according to fat_length " + "and fat32_length fields,\n" + " but has only %lu clusters, less than the required " + "minimum of %d.\n" + " This may lead to problems on some systems.\n", + fs->clusters, FAT16_THRESHOLD); + + check_fat_state_bit(fs, &b); + fs->backupboot_start = CF_LE_W(b.backup_boot) * logical_sector_size; + check_backup_boot(fs, &b, logical_sector_size); + + read_fsinfo(fs, &b, logical_sector_size); + } else if (!atari_format) { + /* On real MS-DOS, a 16 bit FAT is used whenever there would be too + * much clusers otherwise. */ + fs->fat_bits = (fs->clusters >= FAT12_THRESHOLD) ? 16 : 12; + if (fs->clusters >= FAT16_THRESHOLD) + die("Too many clusters (%lu) for FAT16 filesystem.", fs->clusters); + check_fat_state_bit(fs, &b); + } else { + /* On Atari, things are more difficult: GEMDOS always uses 12bit FATs + * on floppies, and always 16 bit on harddisks. */ + fs->fat_bits = 16; /* assume 16 bit FAT for now */ + /* If more clusters than fat entries in 16-bit fat, we assume + * it's a real MSDOS FS with 12-bit fat. */ + if (fs->clusters + 2 > fat_length * logical_sector_size * 8 / 16 || + /* if it's a floppy disk --> 12bit fat */ + device_no == 2 || + /* if it's a ramdisk or loopback device and has one of the usual + * floppy sizes -> 12bit FAT */ + ((device_no == 1 || device_no == 7) && + (total_sectors == 720 || total_sectors == 1440 || + total_sectors == 2880))) + fs->fat_bits = 12; + } + /* On FAT32, the high 4 bits of a FAT entry are reserved */ + fs->eff_fat_bits = (fs->fat_bits == 32) ? 28 : fs->fat_bits; + fs->fat_size = fat_length * logical_sector_size; + + fs->label = calloc(12, sizeof(__u8)); + if (fs->fat_bits == 12 || fs->fat_bits == 16) { + struct boot_sector_16 *b16 = (struct boot_sector_16 *)&b; + if (b16->extended_sig == 0x29) + memmove(fs->label, b16->label, 11); + else + fs->label = NULL; + } else if (fs->fat_bits == 32) { + if (b.extended_sig == 0x29) + memmove(fs->label, &b.label, 11); + else + fs->label = NULL; + } + + if (fs->clusters > + ((unsigned long long)fs->fat_size * 8 / fs->fat_bits) - 2) + die("File system has %d clusters but only space for %d FAT entries.", + fs->clusters, + ((unsigned long long)fs->fat_size * 8 / fs->fat_bits) - 2); + if (!fs->root_entries && !fs->root_cluster) + die("Root directory has zero size."); + if (fs->root_entries & (MSDOS_DPS - 1)) + die("Root directory (%d entries) doesn't span an integral number of " + "sectors.", fs->root_entries); + if (logical_sector_size & (SECTOR_SIZE - 1)) + die("Logical sector size (%d bytes) is not a multiple of the physical " + "sector size.", logical_sector_size); +#if 0 /* linux kernel doesn't check that either */ + /* ++roman: On Atari, these two fields are often left uninitialized */ + if (!atari_format && (!b.secs_track || !b.heads)) + die("Invalid disk format in boot sector."); +#endif + if (verbose) + dump_boot(fs, &b, logical_sector_size); +} + +static void write_boot_label(DOS_FS * fs, char *label) +{ + struct boot_sector b; + struct boot_sector_16 *b16 = (struct boot_sector_16 *)&b; + + fs_read(0, sizeof(b), &b); + if (fs->fat_bits == 12 || fs->fat_bits == 16) { + if (b16->extended_sig != 0x29) { + b16->extended_sig = 0x29; + b16->serial = 0; + memmove(b16->fs_type, fs->fat_bits == 12 ? "FAT12 " : "FAT16 ", + 8); + } + memmove(b16->label, label, 11); + } else if (fs->fat_bits == 32) { + if (b.extended_sig != 0x29) { + b.extended_sig = 0x29; + b.serial = 0; + memmove(b.fs_type, "FAT32 ", 8); + } + memmove(b.label, label, 11); + } + fs_write(0, sizeof(b), &b); + if (fs->fat_bits == 32 && fs->backupboot_start) + fs_write(fs->backupboot_start, sizeof(b), &b); +} + +loff_t find_volume_de(DOS_FS * fs, DIR_ENT * de) +{ + unsigned long cluster; + loff_t offset; + int i; + + if (fs->root_cluster) { + for (cluster = fs->root_cluster; + cluster != 0 && cluster != -1; + cluster = next_cluster(fs, cluster)) { + offset = cluster_start(fs, cluster); + for (i = 0; i * sizeof(DIR_ENT) < fs->cluster_size; i++) { + fs_read(offset, sizeof(DIR_ENT), de); + if (de->attr & ATTR_VOLUME) + return offset; + offset += sizeof(DIR_ENT); + } + } + } else { + for (i = 0; i < fs->root_entries; i++) { + offset = fs->root_start + i * sizeof(DIR_ENT); + fs_read(offset, sizeof(DIR_ENT), de); + if (de->attr & ATTR_VOLUME) + return offset; + } + } + + return 0; +} + +static void write_volume_label(DOS_FS * fs, char *label) +{ + time_t now = time(NULL); + struct tm *mtime = localtime(&now); + loff_t offset; + int created; + DIR_ENT de; + + created = 0; + offset = find_volume_de(fs, &de); + if (offset == 0) + { + created = 1; + offset = alloc_rootdir_entry(fs, &de, label); + } + memcpy(de.name, label, 11); + de.time = CT_LE_W((unsigned short)((mtime->tm_sec >> 1) + + (mtime->tm_min << 5) + + (mtime->tm_hour << 11))); + de.date = CT_LE_W((unsigned short)(mtime->tm_mday + + ((mtime->tm_mon + 1) << 5) + + ((mtime->tm_year - 80) << 9))); + if (created) + { + de.attr = ATTR_VOLUME; + de.ctime_ms = 0; + de.ctime = de.time; + de.cdate = de.date; + de.adate = de.date; + de.starthi = CT_LE_W(0); + de.start = CT_LE_W(0); + de.size = CT_LE_L(0); + } + + fs_write(offset, sizeof(DIR_ENT), &de); +} + +void write_label(DOS_FS * fs, char *label) +{ + int l = strlen(label); + + while (l < 11) + label[l++] = ' '; + + write_boot_label(fs, label); + write_volume_label(fs, label); +} diff --git a/src/boot.h b/src/boot.h new file mode 100644 index 0000000..6e2d6c2 --- /dev/null +++ b/src/boot.h @@ -0,0 +1,32 @@ +/* boot.h - Read and analyze ia PC/MS-DOS boot sector + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +#ifndef _BOOT_H +#define _BOOT_H + +void read_boot(DOS_FS * fs); +void write_label(DOS_FS * fs, char *label); +loff_t find_volume_de(DOS_FS *fs, DIR_ENT *de); + +/* Reads the boot sector from the currently open device and initializes *FS */ + +#endif diff --git a/src/charconv.c b/src/charconv.c new file mode 100644 index 0000000..e869e41 --- /dev/null +++ b/src/charconv.c @@ -0,0 +1,59 @@ +#include "charconv.h" +#include +#include +#include +#include + +static iconv_t iconv_init_codepage(int codepage) +{ + iconv_t result; + char codepage_name[16]; + snprintf(codepage_name, sizeof(codepage_name), "CP%d", codepage); + result = iconv_open(nl_langinfo(CODESET), codepage_name); + if (result == (iconv_t) - 1) + perror(codepage_name); + return result; +} + +static iconv_t dos_to_local; + +/* + * Initialize conversion from codepage. + * codepage = -1 means default codepage. + * Returns 0 on success, non-zero on failure + */ +static int init_conversion(int codepage) +{ + static int initialized = -1; + if (initialized < 0) { + initialized = 1; + if (codepage < 0) + codepage = DEFAULT_DOS_CODEPAGE; + setlocale(LC_ALL, ""); /* initialize locale */ + dos_to_local = iconv_init_codepage(codepage); + if (dos_to_local == (iconv_t) - 1 && codepage != DEFAULT_DOS_CODEPAGE) { + printf("Trying to set fallback DOS codepage %d\n", + DEFAULT_DOS_CODEPAGE); + dos_to_local = iconv_init_codepage(DEFAULT_DOS_CODEPAGE); + if (dos_to_local == (iconv_t) - 1) + initialized = 0; /* no conversion available */ + } + } + return initialized; +} + +int set_dos_codepage(int codepage) +{ + return init_conversion(codepage); +} + +int dos_char_to_printable(char **p, unsigned char c) +{ + char in[1] = { c }; + char *pin = in; + size_t bytes_in = 1; + size_t bytes_out = 4; + if (!init_conversion(-1)) + return 0; + return iconv(dos_to_local, &pin, &bytes_in, p, &bytes_out) != -1; +} diff --git a/src/charconv.h b/src/charconv.h new file mode 100644 index 0000000..3ac3e56 --- /dev/null +++ b/src/charconv.h @@ -0,0 +1,9 @@ +#ifndef _CHARCONV_H +#define _CHARCONV_H + +#define DEFAULT_DOS_CODEPAGE 437 + +int set_dos_codepage(int codepage); +int dos_char_to_printable(char **p, unsigned char c); + +#endif diff --git a/src/check.c b/src/check.c new file mode 100644 index 0000000..1ae91c6 --- /dev/null +++ b/src/check.c @@ -0,0 +1,1055 @@ +/* check.c - Check and repair a PC/MS-DOS file system + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek */ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "dosfsck.h" +#include "io.h" +#include "fat.h" +#include "file.h" +#include "lfn.h" +#include "check.h" + +static DOS_FILE *root; + +/* get start field of a dir entry */ +#define FSTART(p,fs) \ + ((unsigned long)CF_LE_W(p->dir_ent.start) | \ + (fs->fat_bits == 32 ? CF_LE_W(p->dir_ent.starthi) << 16 : 0)) + +#define MODIFY(p,i,v) \ + do { \ + if (p->offset) { \ + p->dir_ent.i = v; \ + fs_write(p->offset+offsetof(DIR_ENT,i), \ + sizeof(p->dir_ent.i),&p->dir_ent.i); \ + } \ + } while(0) + +#define MODIFY_START(p,v,fs) \ + do { \ + unsigned long __v = (v); \ + if (!p->offset) { \ + /* writing to fake entry for FAT32 root dir */ \ + if (!__v) die("Oops, deleting FAT32 root dir!"); \ + fs->root_cluster = __v; \ + p->dir_ent.start = CT_LE_W(__v&0xffff); \ + p->dir_ent.starthi = CT_LE_W(__v>>16); \ + __v = CT_LE_L(__v); \ + fs_write((loff_t)offsetof(struct boot_sector,root_cluster), \ + sizeof(((struct boot_sector *)0)->root_cluster), \ + &__v); \ + } \ + else { \ + MODIFY(p,start,CT_LE_W((__v)&0xffff)); \ + if (fs->fat_bits == 32) \ + MODIFY(p,starthi,CT_LE_W((__v)>>16)); \ + } \ + } while(0) + +loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern) +{ + static int curr_num = 0; + loff_t offset; + + if (fs->root_cluster) { + DIR_ENT d2; + int i = 0, got = 0; + unsigned long clu_num, prev = 0; + loff_t offset2; + + clu_num = fs->root_cluster; + offset = cluster_start(fs, clu_num); + while (clu_num > 0 && clu_num != -1) { + fs_read(offset, sizeof(DIR_ENT), &d2); + if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) { + got = 1; + break; + } + i += sizeof(DIR_ENT); + offset += sizeof(DIR_ENT); + if ((i % fs->cluster_size) == 0) { + prev = clu_num; + if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1) + break; + offset = cluster_start(fs, clu_num); + } + } + if (!got) { + /* no free slot, need to extend root dir: alloc next free cluster + * after previous one */ + if (!prev) + die("Root directory has no cluster allocated!"); + for (clu_num = prev + 1; clu_num != prev; clu_num++) { + FAT_ENTRY entry; + + if (clu_num >= fs->clusters + 2) + clu_num = 2; + get_fat(&entry, fs->fat, clu_num, fs); + if (!entry.value) + break; + } + if (clu_num == prev) + die("Root directory full and no free cluster"); + set_fat(fs, prev, clu_num); + set_fat(fs, clu_num, -1); + set_owner(fs, clu_num, get_owner(fs, fs->root_cluster)); + /* clear new cluster */ + memset(&d2, 0, sizeof(d2)); + offset = cluster_start(fs, clu_num); + for (i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT)) + fs_write(offset + i, sizeof(d2), &d2); + } + memset(de, 0, sizeof(DIR_ENT)); + while (1) { + char expanded[12]; + sprintf(expanded, pattern, curr_num); + memcpy(de->name, expanded, 8); + memcpy(de->ext, expanded + 8, 3); + clu_num = fs->root_cluster; + i = 0; + offset2 = cluster_start(fs, clu_num); + while (clu_num > 0 && clu_num != -1) { + fs_read(offset2, sizeof(DIR_ENT), &d2); + if (offset2 != offset && + !strncmp((const char *)d2.name, (const char *)de->name, + MSDOS_NAME)) + break; + i += sizeof(DIR_ENT); + offset2 += sizeof(DIR_ENT); + if ((i % fs->cluster_size) == 0) { + if ((clu_num = next_cluster(fs, clu_num)) == 0 || + clu_num == -1) + break; + offset2 = cluster_start(fs, clu_num); + } + } + if (clu_num == 0 || clu_num == -1) + break; + if (++curr_num >= 10000) + die("Unable to create unique name"); + } + } else { + DIR_ENT *root; + int next_free = 0, scan; + + root = alloc(fs->root_entries * sizeof(DIR_ENT)); + fs_read(fs->root_start, fs->root_entries * sizeof(DIR_ENT), root); + + while (next_free < fs->root_entries) + if (IS_FREE(root[next_free].name) && + root[next_free].attr != VFAT_LN_ATTR) + break; + else + next_free++; + if (next_free == fs->root_entries) + die("Root directory is full."); + offset = fs->root_start + next_free * sizeof(DIR_ENT); + memset(de, 0, sizeof(DIR_ENT)); + while (1) { + char expanded[12]; + sprintf(expanded, pattern, curr_num); + memcpy(de->name, expanded, 8); + memcpy(de->ext, expanded + 8, 3); + for (scan = 0; scan < fs->root_entries; scan++) + if (scan != next_free && + !strncmp((const char *)root[scan].name, + (const char *)de->name, MSDOS_NAME)) + break; + if (scan == fs->root_entries) + break; + if (++curr_num >= 10000) + die("Unable to create unique name"); + } + free(root); + } + ++n_files; + return offset; +} + +/** + * Construct a full path (starting with '/') for the specified dentry, + * relative to the partition. All components are "long" names where possible. + * + * @param[in] file Information about dentry (file or directory) of interest + * + * return Pointer to static string containing file's full path + */ +static char *path_name(DOS_FILE * file) +{ + static char path[PATH_MAX * 2]; + + if (!file) + *path = 0; /* Reached the root directory */ + else { + if (strlen(path_name(file->parent)) > PATH_MAX) + die("Path name too long."); + if (strcmp(path, "/") != 0) + strcat(path, "/"); + + /* Append the long name to the path, + * or the short name if there isn't a long one + */ + strcpy(strrchr(path, 0), + file->lfn ? file->lfn : file_name(file->dir_ent.name)); + } + return path; +} + +static int day_n[] = + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 }; + /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ + +/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ + +time_t date_dos2unix(unsigned short time, unsigned short date) +{ + int month, year; + time_t secs; + + month = ((date >> 5) & 15) - 1; + year = date >> 9; + secs = + (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + + 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - + ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653); + /* days since 1.1.70 plus 80's leap day */ + return secs; +} + +static char *file_stat(DOS_FILE * file) +{ + static char temp[100]; + struct tm *tm; + char tmp[100]; + time_t date; + + date = + date_dos2unix(CF_LE_W(file->dir_ent.time), CF_LE_W(file->dir_ent.date)); + tm = localtime(&date); + strftime(tmp, 99, "%H:%M:%S %b %d %Y", tm); + sprintf(temp, " Size %u bytes, date %s", CF_LE_L(file->dir_ent.size), tmp); + return temp; +} + +static int bad_name(DOS_FILE * file) +{ + int i, spc, suspicious = 0; + char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:"; + unsigned char *name = file->dir_ent.name; + + /* Do not complain about (and auto-correct) the extended attribute files + * of OS/2. */ + if (strncmp((const char *)name, "EA DATA SF", 11) == 0 || + strncmp((const char *)name, "WP ROOT SF", 11) == 0) + return 0; + + /* don't complain about the dummy 11 bytes used by patched Linux + kernels */ + if (file->dir_ent.lcase & FAT_NO_83NAME) + return 0; + + for (i = 0; i < 8; i++) { + if (name[i] < ' ' || name[i] == 0x7f) + return 1; + if (name[i] > 0x7f) + ++suspicious; + if (strchr(bad_chars, name[i])) + return 1; + } + + for (i = 8; i < 11; i++) { + if (name[i] < ' ' || name[i] == 0x7f) + return 1; + if (name[i] > 0x7f) + ++suspicious; + if (strchr(bad_chars, name[i])) + return 1; + } + + spc = 0; + for (i = 0; i < 8; i++) { + if (name[i] == ' ') + spc = 1; + else if (spc) + /* non-space after a space not allowed, space terminates the name + * part */ + return 1; + } + + spc = 0; + for (i = 8; i < 11; i++) { + if (name[i] == ' ') + spc = 1; + else if (spc) + /* non-space after a space not allowed, space terminates the name + * part */ + return 1; + } + + /* Under GEMDOS, chars >= 128 are never allowed. */ + if (atari_format && suspicious) + return 1; + + /* Under MS-DOS and Windows, chars >= 128 in short names are valid + * (but these characters can be visualised differently depending on + * local codepage: CP437, CP866, etc). The chars are all basically ok, + * so we shouldn't auto-correct such names. */ + return 0; +} + +static void lfn_remove(loff_t from, loff_t to) +{ + DIR_ENT empty; + + /* New dir entry is zeroed except first byte, which is set to 0xe5. + * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading + * a directory at the first zero entry... + */ + memset(&empty, 0, sizeof(empty)); + empty.name[0] = DELETED_FLAG; + + for (; from < to; from += sizeof(empty)) { + fs_write(from, sizeof(DIR_ENT), &empty); + } +} + +static void drop_file(DOS_FS * fs, DOS_FILE * file) +{ + unsigned long cluster; + + MODIFY(file, name[0], DELETED_FLAG); + if (file->lfn) + lfn_remove(file->lfn_offset, file->offset); + for (cluster = FSTART(file, fs); cluster > 0 && cluster < + fs->clusters + 2; cluster = next_cluster(fs, cluster)) + set_owner(fs, cluster, NULL); + --n_files; +} + +static void truncate_file(DOS_FS * fs, DOS_FILE * file, unsigned long clusters) +{ + int deleting; + unsigned long walk, next, prev; + + walk = FSTART(file, fs); + prev = 0; + if ((deleting = !clusters)) + MODIFY_START(file, 0, fs); + while (walk > 0 && walk != -1) { + next = next_cluster(fs, walk); + if (deleting) + set_fat(fs, walk, 0); + else if ((deleting = !--clusters)) + set_fat(fs, walk, -1); + prev = walk; + walk = next; + } +} + +static void auto_rename(DOS_FILE * file) +{ + DOS_FILE *first, *walk; + unsigned long int number; + + if (!file->offset) + return; /* cannot rename FAT32 root dir */ + first = file->parent ? file->parent->first : root; + number = 0; + while (1) { + char num[8]; + sprintf(num, "%07lu", number); + memcpy(file->dir_ent.name, "FSCK", 4); + memcpy(file->dir_ent.name + 4, num, 4); + memcpy(file->dir_ent.ext, num + 4, 3); + for (walk = first; walk; walk = walk->next) + if (walk != file + && !strncmp((const char *)walk->dir_ent.name, + (const char *)file->dir_ent.name, MSDOS_NAME)) + break; + if (!walk) { + fs_write(file->offset, MSDOS_NAME, file->dir_ent.name); + if (file->lfn) + lfn_fix_checksum(file->lfn_offset, file->offset, + (const char *)file->dir_ent.name); + return; + } + number++; + if (number > 9999999) { + die("Too many files need repair."); + } + } + die("Can't generate a unique name."); +} + +static void rename_file(DOS_FILE * file) +{ + unsigned char name[46]; + unsigned char *walk, *here; + + if (!file->offset) { + printf("Cannot rename FAT32 root dir\n"); + return; /* cannot rename FAT32 root dir */ + } + while (1) { + printf("New name: "); + fflush(stdout); + if (fgets((char *)name, 45, stdin)) { + if ((here = (unsigned char *)strchr((const char *)name, '\n'))) + *here = 0; + for (walk = (unsigned char *)strrchr((const char *)name, 0); + walk >= name && (*walk == ' ' || *walk == '\t'); walk--) ; + walk[1] = 0; + for (walk = name; *walk == ' ' || *walk == '\t'; walk++) ; + if (file_cvt(walk, file->dir_ent.name)) { + fs_write(file->offset, MSDOS_NAME, file->dir_ent.name); + if (file->lfn) + lfn_fix_checksum(file->lfn_offset, file->offset, + (const char *)file->dir_ent.name); + return; + } + } + } +} + +static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dots) +{ + char *name; + + name = + strncmp((const char *)file->dir_ent.name, MSDOS_DOT, + MSDOS_NAME) ? ".." : "."; + if (!(file->dir_ent.attr & ATTR_DIR)) { + printf("%s\n Is a non-directory.\n", path_name(file)); + if (interactive) + printf("1) Drop it\n2) Auto-rename\n3) Rename\n" + "4) Convert to directory\n"); + else + printf(" Auto-renaming it.\n"); + switch (interactive ? get_key("1234", "?") : '2') { + case '1': + drop_file(fs, file); + return 1; + case '2': + auto_rename(file); + printf(" Renamed to %s\n", file_name(file->dir_ent.name)); + return 0; + case '3': + rename_file(file); + return 0; + case '4': + MODIFY(file, size, CT_LE_L(0)); + MODIFY(file, attr, file->dir_ent.attr | ATTR_DIR); + break; + } + } + if (!dots) { + printf("Root contains directory \"%s\". Dropping it.\n", name); + drop_file(fs, file); + return 1; + } + return 0; +} + +static int check_file(DOS_FS * fs, DOS_FILE * file) +{ + DOS_FILE *owner; + int restart; + unsigned long expect, curr, this, clusters, prev, walk, clusters2; + + if (file->dir_ent.attr & ATTR_DIR) { + if (CF_LE_L(file->dir_ent.size)) { + printf("%s\n Directory has non-zero size. Fixing it.\n", + path_name(file)); + MODIFY(file, size, CT_LE_L(0)); + } + if (file->parent + && !strncmp((const char *)file->dir_ent.name, MSDOS_DOT, + MSDOS_NAME)) { + expect = FSTART(file->parent, fs); + if (FSTART(file, fs) != expect) { + printf("%s\n Start (%ld) does not point to parent (%ld)\n", + path_name(file), FSTART(file, fs), expect); + MODIFY_START(file, expect, fs); + } + return 0; + } + if (file->parent + && !strncmp((const char *)file->dir_ent.name, MSDOS_DOTDOT, + MSDOS_NAME)) { + expect = + file->parent->parent ? FSTART(file->parent->parent, fs) : 0; + if (fs->root_cluster && expect == fs->root_cluster) + expect = 0; + if (FSTART(file, fs) != expect) { + printf("%s\n Start (%lu) does not point to .. (%lu)\n", + path_name(file), FSTART(file, fs), expect); + MODIFY_START(file, expect, fs); + } + return 0; + } + if (FSTART(file, fs) == 0) { + printf("%s\n Start does point to root directory. Deleting dir. \n", + path_name(file)); + MODIFY(file, name[0], DELETED_FLAG); + return 0; + } + } + if (FSTART(file, fs) >= fs->clusters + 2) { + printf + ("%s\n Start cluster beyond limit (%lu > %lu). Truncating file.\n", + path_name(file), FSTART(file, fs), fs->clusters + 1); + if (!file->offset) + die("Bad FAT32 root directory! (bad start cluster)\n"); + MODIFY_START(file, 0, fs); + } + clusters = prev = 0; + for (curr = FSTART(file, fs) ? FSTART(file, fs) : + -1; curr != -1; curr = next_cluster(fs, curr)) { + FAT_ENTRY curEntry; + get_fat(&curEntry, fs->fat, curr, fs); + + if (!curEntry.value || bad_cluster(fs, curr)) { + printf("%s\n Contains a %s cluster (%lu). Assuming EOF.\n", + path_name(file), curEntry.value ? "bad" : "free", curr); + if (prev) + set_fat(fs, prev, -1); + else if (!file->offset) + die("FAT32 root dir starts with a bad cluster!"); + else + MODIFY_START(file, 0, fs); + break; + } + if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) <= + (unsigned long long)clusters * fs->cluster_size) { + printf + ("%s\n File size is %u bytes, cluster chain length is > %llu " + "bytes.\n Truncating file to %u bytes.\n", path_name(file), + CF_LE_L(file->dir_ent.size), + (unsigned long long)clusters * fs->cluster_size, + CF_LE_L(file->dir_ent.size)); + truncate_file(fs, file, clusters); + break; + } + if ((owner = get_owner(fs, curr))) { + int do_trunc = 0; + printf("%s and\n", path_name(owner)); + printf("%s\n share clusters.\n", path_name(file)); + clusters2 = 0; + for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk = + next_cluster(fs, walk)) + if (walk == curr) + break; + else + clusters2++; + restart = file->dir_ent.attr & ATTR_DIR; + if (!owner->offset) { + printf(" Truncating second to %llu bytes because first " + "is FAT32 root dir.\n", + (unsigned long long)clusters2 * fs->cluster_size); + do_trunc = 2; + } else if (!file->offset) { + printf(" Truncating first to %llu bytes because second " + "is FAT32 root dir.\n", + (unsigned long long)clusters * fs->cluster_size); + do_trunc = 1; + } else if (interactive) + printf("1) Truncate first to %llu bytes%s\n" + "2) Truncate second to %llu bytes\n", + (unsigned long long)clusters * fs->cluster_size, + restart ? " and restart" : "", + (unsigned long long)clusters2 * fs->cluster_size); + else + printf(" Truncating second to %llu bytes.\n", + (unsigned long long)clusters2 * fs->cluster_size); + if (do_trunc != 2 + && (do_trunc == 1 + || (interactive && get_key("12", "?") == '1'))) { + prev = 0; + clusters = 0; + for (this = FSTART(owner, fs); this > 0 && this != -1; this = + next_cluster(fs, this)) { + if (this == curr) { + if (prev) + set_fat(fs, prev, -1); + else + MODIFY_START(owner, 0, fs); + MODIFY(owner, size, + CT_LE_L((unsigned long long)clusters * + fs->cluster_size)); + if (restart) + return 1; + while (this > 0 && this != -1) { + set_owner(fs, this, NULL); + this = next_cluster(fs, this); + } + this = curr; + break; + } + clusters++; + prev = this; + } + if (this != curr) + die("Internal error: didn't find cluster %d in chain" + " starting at %d", curr, FSTART(owner, fs)); + } else { + if (prev) + set_fat(fs, prev, -1); + else + MODIFY_START(file, 0, fs); + break; + } + } + set_owner(fs, curr, file); + clusters++; + prev = curr; + } + if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) > + (unsigned long long)clusters * fs->cluster_size) { + printf + ("%s\n File size is %u bytes, cluster chain length is %llu bytes." + "\n Truncating file to %llu bytes.\n", path_name(file), + CF_LE_L(file->dir_ent.size), + (unsigned long long)clusters * fs->cluster_size, + (unsigned long long)clusters * fs->cluster_size); + MODIFY(file, size, + CT_LE_L((unsigned long long)clusters * fs->cluster_size)); + } + return 0; +} + +static int check_files(DOS_FS * fs, DOS_FILE * start) +{ + while (start) { + if (check_file(fs, start)) + return 1; + start = start->next; + } + return 0; +} + +static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots) +{ + DOS_FILE *parent, **walk, **scan; + int dot, dotdot, skip, redo; + int good, bad; + + if (!*root) + return 0; + parent = (*root)->parent; + good = bad = 0; + for (walk = root; *walk; walk = &(*walk)->next) + if (bad_name(*walk)) + bad++; + else + good++; + if (*root && parent && good + bad > 4 && bad > good / 2) { + printf("%s\n Has a large number of bad entries. (%d/%d)\n", + path_name(parent), bad, good + bad); + if (!dots) + printf(" Not dropping root directory.\n"); + else if (!interactive) + printf(" Not dropping it in auto-mode.\n"); + else if (get_key("yn", "Drop directory ? (y/n)") == 'y') { + truncate_file(fs, parent, 0); + MODIFY(parent, name[0], DELETED_FLAG); + /* buglet: deleted directory stays in the list. */ + return 1; + } + } + dot = dotdot = redo = 0; + walk = root; + while (*walk) { + if (!strncmp + ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME) + || !strncmp((const char *)((*walk)->dir_ent.name), MSDOS_DOTDOT, + MSDOS_NAME)) { + if (handle_dot(fs, *walk, dots)) { + *walk = (*walk)->next; + continue; + } + if (!strncmp + ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME)) + dot++; + else + dotdot++; + } + if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) { + puts(path_name(*walk)); + printf(" Bad short file name (%s).\n", + file_name((*walk)->dir_ent.name)); + if (interactive) + printf("1) Drop file\n2) Rename file\n3) Auto-rename\n" + "4) Keep it\n"); + else + printf(" Auto-renaming it.\n"); + switch (interactive ? get_key("1234", "?") : '3') { + case '1': + drop_file(fs, *walk); + walk = &(*walk)->next; + continue; + case '2': + rename_file(*walk); + redo = 1; + break; + case '3': + auto_rename(*walk); + printf(" Renamed to %s\n", file_name((*walk)->dir_ent.name)); + break; + case '4': + break; + } + } + /* don't check for duplicates of the volume label */ + if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) { + scan = &(*walk)->next; + skip = 0; + while (*scan && !skip) { + if (!((*scan)->dir_ent.attr & ATTR_VOLUME) && + !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name, + MSDOS_NAME)) { + printf("%s\n Duplicate directory entry.\n First %s\n", + path_name(*walk), file_stat(*walk)); + printf(" Second %s\n", file_stat(*scan)); + if (interactive) + printf + ("1) Drop first\n2) Drop second\n3) Rename first\n" + "4) Rename second\n5) Auto-rename first\n" + "6) Auto-rename second\n"); + else + printf(" Auto-renaming second.\n"); + switch (interactive ? get_key("123456", "?") : '6') { + case '1': + drop_file(fs, *walk); + *walk = (*walk)->next; + skip = 1; + break; + case '2': + drop_file(fs, *scan); + *scan = (*scan)->next; + continue; + case '3': + rename_file(*walk); + printf(" Renamed to %s\n", path_name(*walk)); + redo = 1; + break; + case '4': + rename_file(*scan); + printf(" Renamed to %s\n", path_name(*walk)); + redo = 1; + break; + case '5': + auto_rename(*walk); + printf(" Renamed to %s\n", + file_name((*walk)->dir_ent.name)); + break; + case '6': + auto_rename(*scan); + printf(" Renamed to %s\n", + file_name((*scan)->dir_ent.name)); + break; + } + } + scan = &(*scan)->next; + } + if (skip) + continue; + } + if (!redo) + walk = &(*walk)->next; + else { + walk = root; + dot = dotdot = redo = 0; + } + } + if (dots && !dot) + printf("%s\n \".\" is missing. Can't fix this yet.\n", + path_name(parent)); + if (dots && !dotdot) + printf("%s\n \"..\" is missing. Can't fix this yet.\n", + path_name(parent)); + return 0; +} + +/** + * Check a dentry's cluster chain for bad clusters. + * If requested, we verify readability and mark unreadable clusters as bad. + * + * @param[inout] fs Information about the filesystem + * @param[in] file dentry to check + * @param[in] read_test Nonzero == verify that dentry's clusters can + * be read + */ +static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test) +{ + DOS_FILE *owner; + unsigned long walk, prev, clusters, next_clu; + + prev = clusters = 0; + for (walk = FSTART(file, fs); walk > 0 && walk < fs->clusters + 2; + walk = next_clu) { + next_clu = next_cluster(fs, walk); + + /* In this stage we are checking only for a loop within our own + * cluster chain. + * Cross-linking of clusters is handled in check_file() + */ + if ((owner = get_owner(fs, walk))) { + if (owner == file) { + printf("%s\n Circular cluster chain. Truncating to %lu " + "cluster%s.\n", path_name(file), clusters, + clusters == 1 ? "" : "s"); + if (prev) + set_fat(fs, prev, -1); + else if (!file->offset) + die("Bad FAT32 root directory! (bad start cluster)\n"); + else + MODIFY_START(file, 0, fs); + } + break; + } + if (bad_cluster(fs, walk)) + break; + if (read_test) { + if (fs_test(cluster_start(fs, walk), fs->cluster_size)) { + prev = walk; + clusters++; + } else { + printf("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n", + path_name(file), clusters, walk); + if (prev) + set_fat(fs, prev, next_cluster(fs, walk)); + else + MODIFY_START(file, next_cluster(fs, walk), fs); + set_fat(fs, walk, -2); + } + } + set_owner(fs, walk, file); + } + /* Revert ownership (for now) */ + for (walk = FSTART(file, fs); walk > 0 && walk < fs->clusters + 2; + walk = next_cluster(fs, walk)) + if (bad_cluster(fs, walk)) + break; + else if (get_owner(fs, walk) == file) + set_owner(fs, walk, NULL); + else + break; +} + +static void undelete(DOS_FS * fs, DOS_FILE * file) +{ + unsigned long clusters, left, prev, walk; + + clusters = left = (CF_LE_L(file->dir_ent.size) + fs->cluster_size - 1) / + fs->cluster_size; + prev = 0; + + walk = FSTART(file, fs); + + while (left && (walk >= 2) && (walk < fs->clusters + 2)) { + + FAT_ENTRY curEntry; + get_fat(&curEntry, fs->fat, walk, fs); + + if (!curEntry.value) + break; + + left--; + if (prev) + set_fat(fs, prev, walk); + prev = walk; + walk++; + } + if (prev) + set_fat(fs, prev, -1); + else + MODIFY_START(file, 0, fs); + if (left) + printf("Warning: Did only undelete %lu of %lu cluster%s.\n", + clusters - left, clusters, clusters == 1 ? "" : "s"); + +} + +static void new_dir(void) +{ + lfn_reset(); +} + +/** + * Create a description for a referenced dentry and insert it in our dentry + * tree. Then, go check the dentry's cluster chain for bad clusters and + * cluster loops. + * + * @param[inout] fs Information about the filesystem + * @param[out] chain + * @param[in] parent Information about parent directory of this file + * NULL == no parent ('file' is root directory) + * @param[in] offset Partition-relative byte offset of directory entry of interest + * 0 == Root directory + * @param cp + */ +static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent, + loff_t offset, FDSC ** cp) +{ + DOS_FILE *new; + DIR_ENT de; + FD_TYPE type; + + if (offset) + fs_read(offset, sizeof(DIR_ENT), &de); + else { + /* Construct a DIR_ENT for the root directory */ + memcpy(de.name, " ", MSDOS_NAME); + de.attr = ATTR_DIR; + de.size = de.time = de.date = 0; + de.start = CT_LE_W(fs->root_cluster & 0xffff); + de.starthi = CT_LE_W((fs->root_cluster >> 16) & 0xffff); + } + if ((type = file_type(cp, (char *)de.name)) != fdt_none) { + if (type == fdt_undelete && (de.attr & ATTR_DIR)) + die("Can't undelete directories."); + file_modify(cp, (char *)de.name); + fs_write(offset, 1, &de); + } + if (IS_FREE(de.name)) { + lfn_check_orphaned(); + return; + } + if (de.attr == VFAT_LN_ATTR) { + lfn_add_slot(&de, offset); + return; + } + new = qalloc(&mem_queue, sizeof(DOS_FILE)); + new->lfn = lfn_get(&de, &new->lfn_offset); + new->offset = offset; + memcpy(&new->dir_ent, &de, sizeof(de)); + new->next = new->first = NULL; + new->parent = parent; + if (type == fdt_undelete) + undelete(fs, new); + **chain = new; + *chain = &new->next; + if (list) { + printf("Checking file %s", path_name(new)); + if (new->lfn) + printf(" (%s)", file_name(new->dir_ent.name)); /* (8.3) */ + printf("\n"); + } + /* Don't include root directory, '.', or '..' in the total file count */ + if (offset && + strncmp((const char *)de.name, MSDOS_DOT, MSDOS_NAME) != 0 && + strncmp((const char *)de.name, MSDOS_DOTDOT, MSDOS_NAME) != 0) + ++n_files; + test_file(fs, new, test); /* Bad cluster check */ +} + +static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp); + +static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp) +{ + DOS_FILE **chain; + int i; + unsigned long clu_num; + + chain = &this->first; + i = 0; + clu_num = FSTART(this, fs); + new_dir(); + while (clu_num > 0 && clu_num != -1) { + add_file(fs, &chain, this, + cluster_start(fs, clu_num) + (i % fs->cluster_size), cp); + i += sizeof(DIR_ENT); + if (!(i % fs->cluster_size)) + if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1) + break; + } + lfn_check_orphaned(); + if (check_dir(fs, &this->first, this->offset)) + return 0; + if (check_files(fs, this->first)) + return 1; + return subdirs(fs, this, cp); +} + +/** + * Recursively scan subdirectories of the specified parent directory. + * + * @param[inout] fs Information about the filesystem + * @param[in] parent Identifies the directory to scan + * @param[in] cp + * + * @return 0 Success + * @return 1 Error + */ +static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp) +{ + DOS_FILE *walk; + + for (walk = parent ? parent->first : root; walk; walk = walk->next) + if (walk->dir_ent.attr & ATTR_DIR) + if (strncmp((const char *)walk->dir_ent.name, MSDOS_DOT, MSDOS_NAME) + && strncmp((const char *)walk->dir_ent.name, MSDOS_DOTDOT, + MSDOS_NAME)) + if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name))) + return 1; + return 0; +} + +/** + * Scan all directory and file information for errors. + * + * @param[inout] fs Information about the filesystem + * + * @return 0 Success + * @return 1 Error + */ +int scan_root(DOS_FS * fs) +{ + DOS_FILE **chain; + int i; + + root = NULL; + chain = &root; + new_dir(); + if (fs->root_cluster) { + add_file(fs, &chain, NULL, 0, &fp_root); + } else { + for (i = 0; i < fs->root_entries; i++) + add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT), + &fp_root); + } + lfn_check_orphaned(); + (void)check_dir(fs, &root, 0); + if (check_files(fs, root)) + return 1; + return subdirs(fs, NULL, &fp_root); +} diff --git a/src/check.h b/src/check.h new file mode 100644 index 0000000..ae05988 --- /dev/null +++ b/src/check.h @@ -0,0 +1,40 @@ +/* check.h - Check and repair a PC/MS-DOS file system + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +#ifndef _CHECK_H +#define _CHECK_H + +loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern); + +/* Allocate a free slot in the root directory for a new file. The file name is + constructed after 'pattern', which must include a %d type format for printf + and expand to exactly 11 characters. The name actually used is written into + the 'de' structure, the rest of *de is cleared. The offset returned is to + where in the filesystem the entry belongs. */ + +int scan_root(DOS_FS * fs); + +/* Scans the root directory and recurses into all subdirectories. See check.c + for all the details. Returns a non-zero integer if the file system has to + be checked again. */ + +#endif diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..09004dd --- /dev/null +++ b/src/common.c @@ -0,0 +1,119 @@ +/* common.c - Common functions + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek */ + +#include +#include +#include +#include +#include + +#include "common.h" + +typedef struct _link { + void *data; + struct _link *next; +} LINK; + +void die(char *msg, ...) +{ + va_list args; + + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + exit(1); +} + +void pdie(char *msg, ...) +{ + va_list args; + + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, ":%s\n", strerror(errno)); + exit(1); +} + +void *alloc(int size) +{ + void *this; + + if ((this = malloc(size))) + return this; + pdie("malloc"); + return NULL; /* for GCC */ +} + +void *qalloc(void **root, int size) +{ + LINK *link; + + link = alloc(sizeof(LINK)); + link->next = *root; + *root = link; + return link->data = alloc(size); +} + +void qfree(void **root) +{ + LINK *this; + + while (*root) { + this = (LINK *) * root; + *root = this->next; + free(this->data); + free(this); + } +} + +int min(int a, int b) +{ + return a < b ? a : b; +} + +char get_key(char *valid, char *prompt) +{ + int ch, okay; + + while (1) { + if (prompt) + printf("%s ", prompt); + fflush(stdout); + while (ch = getchar(), ch == ' ' || ch == '\t') ; + if (ch == EOF) + exit(1); + if (!strchr(valid, okay = ch)) + okay = 0; + while (ch = getchar(), ch != '\n' && ch != EOF) ; + if (ch == EOF) + exit(1); + if (okay) + return okay; + printf("Invalid input.\n"); + } +} diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..7e998ff --- /dev/null +++ b/src/common.h @@ -0,0 +1,58 @@ +/* common.h - Common functions + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +#include + +#ifndef _COMMON_H +#define _COMMON_H + +void die(char *msg, ...) __attribute((noreturn)); + +/* Displays a prinf-style message and terminates the program. */ + +void pdie(char *msg, ...) __attribute((noreturn)); + +/* Like die, but appends an error message according to the state of errno. */ + +void *alloc(int size); + +/* mallocs SIZE bytes and returns a pointer to the data. Terminates the program + if malloc fails. */ + +void *qalloc(void **root, int size); + +/* Like alloc, but registers the data area in a list described by ROOT. */ + +void qfree(void **root); + +/* Deallocates all qalloc'ed data areas described by ROOT. */ + +int min(int a, int b); + +/* Returns the smaller integer value of a and b. */ + +char get_key(char *valid, char *prompt); + +/* Displays PROMPT and waits for user input. Only characters in VALID are + accepted. Terminates the program on EOF. Returns the character. */ + +#endif diff --git a/src/dosfsck.c b/src/dosfsck.c new file mode 100644 index 0000000..84ecdbb --- /dev/null +++ b/src/dosfsck.c @@ -0,0 +1,224 @@ +/* dosfsck.c - User interface + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek */ + +#include "version.h" + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "dosfsck.h" +#include "io.h" +#include "boot.h" +#include "fat.h" +#include "file.h" +#include "check.h" +#include "charconv.h" + +int interactive = 0, rw = 0, list = 0, test = 0, verbose = 0, write_immed = 0; +int atari_format = 0, boot_only = 0; +unsigned n_files = 0; +void *mem_queue = NULL; + +static void usage(char *name) +{ + fprintf(stderr, "usage: %s [-aAbflrtvVwy] [-d path -d ...] " + "[-u path -u ...]\n%15sdevice\n", name, ""); + fprintf(stderr, " -a automatically repair the file system\n"); + fprintf(stderr, " -A toggle Atari file system format\n"); + fprintf(stderr, " -b make read-only boot sector check\n"); + fprintf(stderr, " -c N use DOS codepage N to decode short file names (default: %d)\n", DEFAULT_DOS_CODEPAGE); + fprintf(stderr, " -d path drop that file\n"); + fprintf(stderr, " -f salvage unused chains to files\n"); + fprintf(stderr, " -l list path names\n"); + fprintf(stderr, + " -n no-op, check non-interactively without changing\n"); + fprintf(stderr, " -p same as -a, for compat with other *fsck\n"); + fprintf(stderr, " -r interactively repair the file system\n"); + fprintf(stderr, " -t test for bad clusters\n"); + fprintf(stderr, " -u path try to undelete that (non-directory) file\n"); + fprintf(stderr, " -v verbose mode\n"); + fprintf(stderr, " -V perform a verification pass\n"); + fprintf(stderr, " -w write changes to disk immediately\n"); + fprintf(stderr, " -y same as -a, for compat with other *fsck\n"); + exit(2); +} + +/* + * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant + * of MS-DOS filesystem by default. + */ +static void check_atari(void) +{ +#ifdef __mc68000__ + FILE *f; + char line[128], *p; + + if (!(f = fopen("/proc/hardware", "r"))) { + perror("/proc/hardware"); + return; + } + + while (fgets(line, sizeof(line), f)) { + if (strncmp(line, "Model:", 6) == 0) { + p = line + 6; + p += strspn(p, " \t"); + if (strncmp(p, "Atari ", 6) == 0) + atari_format = 1; + break; + } + } + fclose(f); +#endif +} + +int main(int argc, char **argv) +{ + DOS_FS fs; + int salvage_files, verify, c; + unsigned n_files_check = 0, n_files_verify = 0; + unsigned long free_clusters; + + memset(&fs, 0, sizeof(fs)); + rw = salvage_files = verify = 0; + interactive = 1; + check_atari(); + + while ((c = getopt(argc, argv, "Aac:d:bflnprtu:vVwy")) != EOF) + switch (c) { + case 'A': /* toggle Atari format */ + atari_format = !atari_format; + break; + case 'a': + case 'p': + case 'y': + rw = 1; + interactive = 0; + salvage_files = 1; + break; + case 'b': + rw = 0; + interactive = 0; + boot_only = 1; + break; + case 'c': + set_dos_codepage(atoi(optarg)); + break; + case 'd': + file_add(optarg, fdt_drop); + break; + case 'f': + salvage_files = 1; + break; + case 'l': + list = 1; + break; + case 'n': + rw = 0; + interactive = 0; + break; + case 'r': + rw = 1; + interactive = 1; + break; + case 't': + test = 1; + break; + case 'u': + file_add(optarg, fdt_undelete); + break; + case 'v': + verbose = 1; + printf("dosfsck " VERSION " (" VERSION_DATE ")\n"); + break; + case 'V': + verify = 1; + break; + case 'w': + write_immed = 1; + break; + default: + usage(argv[0]); + } + set_dos_codepage(-1); /* set default codepage if none was given in command line */ + if ((test || write_immed) && !rw) { + fprintf(stderr, "-t and -w require -a or -r\n"); + exit(2); + } + if (optind != argc - 1) + usage(argv[0]); + + printf("dosfsck " VERSION ", " VERSION_DATE ", FAT32, LFN\n"); + fs_open(argv[optind], rw); + + read_boot(&fs); + if (boot_only) + goto exit; + + if (verify) + printf("Starting check/repair pass.\n"); + while (read_fat(&fs), scan_root(&fs)) + qfree(&mem_queue); + if (test) + fix_bad(&fs); + if (salvage_files) + reclaim_file(&fs); + else + reclaim_free(&fs); + free_clusters = update_free(&fs); + file_unused(); + qfree(&mem_queue); + n_files_check = n_files; + if (verify) { + n_files = 0; + printf("Starting verification pass.\n"); + read_fat(&fs); + scan_root(&fs); + reclaim_free(&fs); + qfree(&mem_queue); + n_files_verify = n_files; + } + +exit: + if (fs_changed()) { + if (rw) { + if (interactive) + rw = get_key("yn", "Perform changes ? (y/n)") == 'y'; + else + printf("Performing changes.\n"); + } else + printf("Leaving file system unchanged.\n"); + } + + printf("%s: %u files, %lu/%lu clusters\n", argv[optind], + n_files, fs.clusters - free_clusters, fs.clusters); + + return fs_close(rw) ? 1 : 0; +} diff --git a/src/dosfsck.h b/src/dosfsck.h new file mode 100644 index 0000000..9fe103a --- /dev/null +++ b/src/dosfsck.h @@ -0,0 +1,216 @@ +/* dosfsck.h - Common data structures and global variables + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek */ + +#ifndef _DOSFSCK_H +#define _DOSFSCK_H + +#include +#define _LINUX_STAT_H /* hack to avoid inclusion of */ +#define _LINUX_STRING_H_ /* hack to avoid inclusion of */ +#define _LINUX_FS_H /* hack to avoid inclusion of */ + +#include +#include + +#include + +#undef CF_LE_W +#undef CF_LE_L +#undef CT_LE_W +#undef CT_LE_L + +#if __BYTE_ORDER == __BIG_ENDIAN +#include +#define CF_LE_W(v) bswap_16(v) +#define CF_LE_L(v) bswap_32(v) +#define CT_LE_W(v) CF_LE_W(v) +#define CT_LE_L(v) CF_LE_L(v) +#else +#define CF_LE_W(v) (v) +#define CF_LE_L(v) (v) +#define CT_LE_W(v) (v) +#define CT_LE_L(v) (v) +#endif /* __BIG_ENDIAN */ + +#define VFAT_LN_ATTR (ATTR_RO | ATTR_HIDDEN | ATTR_SYS | ATTR_VOLUME) + +#define FAT_STATE_DIRTY 0x01 + +/* ++roman: Use own definition of boot sector structure -- the kernel headers' + * name for it is msdos_boot_sector in 2.0 and fat_boot_sector in 2.1 ... */ +struct boot_sector { + __u8 ignored[3]; /* Boot strap short or near jump */ + __u8 system_id[8]; /* Name - can be used to special case + partition manager volumes */ + __u8 sector_size[2]; /* bytes per logical sector */ + __u8 cluster_size; /* sectors/cluster */ + __u16 reserved; /* reserved sectors */ + __u8 fats; /* number of FATs */ + __u8 dir_entries[2]; /* root directory entries */ + __u8 sectors[2]; /* number of sectors */ + __u8 media; /* media code (unused) */ + __u16 fat_length; /* sectors/FAT */ + __u16 secs_track; /* sectors per track */ + __u16 heads; /* number of heads */ + __u32 hidden; /* hidden sectors (unused) */ + __u32 total_sect; /* number of sectors (if sectors == 0) */ + + /* The following fields are only used by FAT32 */ + __u32 fat32_length; /* sectors/FAT */ + __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ + __u8 version[2]; /* major, minor filesystem version */ + __u32 root_cluster; /* first cluster in root directory */ + __u16 info_sector; /* filesystem info sector */ + __u16 backup_boot; /* backup boot sector */ + __u8 reserved2[12]; /* Unused */ + + __u8 drive_number; /* Logical Drive Number */ + __u8 reserved3; /* Unused */ + + __u8 extended_sig; /* Extended Signature (0x29) */ + __u32 serial; /* Serial number */ + __u8 label[11]; /* FS label */ + __u8 fs_type[8]; /* FS Type */ + + /* fill up to 512 bytes */ + __u8 junk[422]; +} __attribute__ ((packed)); + +struct boot_sector_16 { + __u8 ignored[3]; /* Boot strap short or near jump */ + __u8 system_id[8]; /* Name - can be used to special case + partition manager volumes */ + __u8 sector_size[2]; /* bytes per logical sector */ + __u8 cluster_size; /* sectors/cluster */ + __u16 reserved; /* reserved sectors */ + __u8 fats; /* number of FATs */ + __u8 dir_entries[2]; /* root directory entries */ + __u8 sectors[2]; /* number of sectors */ + __u8 media; /* media code (unused) */ + __u16 fat_length; /* sectors/FAT */ + __u16 secs_track; /* sectors per track */ + __u16 heads; /* number of heads */ + __u32 hidden; /* hidden sectors (unused) */ + __u32 total_sect; /* number of sectors (if sectors == 0) */ + + __u8 drive_number; /* Logical Drive Number */ + __u8 reserved2; /* Unused */ + + __u8 extended_sig; /* Extended Signature (0x29) */ + __u32 serial; /* Serial number */ + __u8 label[11]; /* FS label */ + __u8 fs_type[8]; /* FS Type */ + + /* fill up to 512 bytes */ + __u8 junk[450]; +} __attribute__ ((packed)); + +struct info_sector { + __u32 magic; /* Magic for info sector ('RRaA') */ + __u8 junk[0x1dc]; + __u32 reserved1; /* Nothing as far as I can tell */ + __u32 signature; /* 0x61417272 ('rrAa') */ + __u32 free_clusters; /* Free cluster count. -1 if unknown */ + __u32 next_cluster; /* Most recently allocated cluster. */ + __u32 reserved2[3]; + __u16 reserved3; + __u16 boot_sign; +}; + +typedef struct { + __u8 name[8], ext[3]; /* name and extension */ + __u8 attr; /* attribute bits */ + __u8 lcase; /* Case for base and extension */ + __u8 ctime_ms; /* Creation time, milliseconds */ + __u16 ctime; /* Creation time */ + __u16 cdate; /* Creation date */ + __u16 adate; /* Last access date */ + __u16 starthi; /* High 16 bits of cluster in FAT32 */ + __u16 time, date, start; /* time, date and first cluster */ + __u32 size; /* file size (in bytes) */ +} __attribute__ ((packed)) DIR_ENT; + +typedef struct _dos_file { + DIR_ENT dir_ent; + char *lfn; + loff_t offset; + loff_t lfn_offset; + struct _dos_file *parent; /* parent directory */ + struct _dos_file *next; /* next entry */ + struct _dos_file *first; /* first entry (directory only) */ +} DOS_FILE; + +typedef struct { + unsigned long value; + unsigned long reserved; +} FAT_ENTRY; + +typedef struct { + int nfats; + loff_t fat_start; + unsigned int fat_size; /* unit is bytes */ + unsigned int fat_bits; /* size of a FAT entry */ + unsigned int eff_fat_bits; /* # of used bits in a FAT entry */ + unsigned long root_cluster; /* 0 for old-style root dir */ + loff_t root_start; + unsigned int root_entries; + loff_t data_start; + unsigned int cluster_size; + unsigned long clusters; + loff_t fsinfo_start; /* 0 if not present */ + long free_clusters; + loff_t backupboot_start; /* 0 if not present */ + unsigned char *fat; + DOS_FILE **cluster_owner; + char *label; +} DOS_FS; + +#ifndef offsetof +#define offsetof(t,e) ((int)&(((t *)0)->e)) +#endif + +extern int interactive, rw, list, verbose, test, write_immed; +extern int atari_format; +extern unsigned n_files; +extern void *mem_queue; + +/* value to use as end-of-file marker */ +#define FAT_EOF(fs) ((atari_format ? 0xfff : 0xff8) | FAT_EXTD(fs)) +#define FAT_IS_EOF(fs,v) ((unsigned long)(v) >= (0xff8|FAT_EXTD(fs))) +/* value to mark bad clusters */ +#define FAT_BAD(fs) (0xff7 | FAT_EXTD(fs)) +/* range of values used for bad clusters */ +#define FAT_MIN_BAD(fs) ((atari_format ? 0xff0 : 0xff7) | FAT_EXTD(fs)) +#define FAT_MAX_BAD(fs) ((atari_format ? 0xff7 : 0xff7) | FAT_EXTD(fs)) +#define FAT_IS_BAD(fs,v) ((v) >= FAT_MIN_BAD(fs) && (v) <= FAT_MAX_BAD(fs)) + +/* return -16 as a number with fs->fat_bits bits */ +#define FAT_EXTD(fs) (((1 << fs->eff_fat_bits)-1) & ~0xf) + +/* marker for files with no 8.3 name */ +#define FAT_NO_83NAME 32 + +#endif diff --git a/src/dosfslabel.c b/src/dosfslabel.c new file mode 100644 index 0000000..dd8d36a --- /dev/null +++ b/src/dosfslabel.c @@ -0,0 +1,144 @@ +/* dosfslabel.c - User interface + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2007 Red Hat, Inc. + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +#include "version.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "dosfsck.h" +#include "io.h" +#include "boot.h" +#include "fat.h" +#include "file.h" +#include "check.h" + +int interactive = 0, rw = 0, list = 0, test = 0, verbose = 0, write_immed = 0; +int atari_format = 0; +unsigned n_files = 0; +void *mem_queue = NULL; + +static void usage(int error) +{ + FILE *f = error ? stderr : stdout; + int status = error ? 1 : 0; + + fprintf(f, "usage: dosfslabel device [label]\n"); + exit(status); +} + +/* + * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant + * of MS-DOS filesystem by default. + */ +static void check_atari(void) +{ +#ifdef __mc68000__ + FILE *f; + char line[128], *p; + + if (!(f = fopen("/proc/hardware", "r"))) { + perror("/proc/hardware"); + return; + } + + while (fgets(line, sizeof(line), f)) { + if (strncmp(line, "Model:", 6) == 0) { + p = line + 6; + p += strspn(p, " \t"); + if (strncmp(p, "Atari ", 6) == 0) + atari_format = 1; + break; + } + } + fclose(f); +#endif +} + +int main(int argc, char *argv[]) +{ + DOS_FS fs = {0}; + rw = 0; + + int i; + + char *device = NULL; + char *label = NULL; + + loff_t offset; + DIR_ENT de; + + check_atari(); + + if (argc < 2 || argc > 3) + usage(1); + + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) + usage(0); + else if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) { + printf("dosfslabel " VERSION ", " VERSION_DATE ", FAT32, LFN\n"); + exit(0); + } + + device = argv[1]; + if (argc == 3) { + label = argv[2]; + if (strlen(label) > 11) { + fprintf(stderr, + "dosfslabel: labels can be no longer than 11 characters\n"); + exit(1); + } + for (i = 0; i < 11; i++) + /* don't know if here should be more strict !uppercase(label[i])*/ + if (islower(label[i])) { + fprintf(stderr, + "dosfslabel: labels cannot contain lower case characters\n"); + exit(1); + } + rw = 1; + } + + fs_open(device, rw); + read_boot(&fs); + if (fs.fat_bits == 32) + read_fat(&fs); + if (!rw) { + offset = find_volume_de(&fs, &de); + if (offset == 0) + fprintf(stdout, "%s\n", fs.label); + else + fprintf(stdout, "%.8s%.3s\n", de.name, de.ext); + exit(0); + } + + write_label(&fs, label); + fs_close(rw); + return 0; +} diff --git a/src/fat.c b/src/fat.c new file mode 100644 index 0000000..a735458 --- /dev/null +++ b/src/fat.c @@ -0,0 +1,550 @@ +/* fat.c - Read/write access to the FAT + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek */ + +#include +#include +#include +#include + +#include "common.h" +#include "dosfsck.h" +#include "io.h" +#include "check.h" +#include "fat.h" + +/** + * Fetch the FAT entry for a specified cluster. + * + * @param[out] entry Cluster to which cluster of interest is linked + * @param[in] fat FAT table for the partition + * @param[in] cluster Cluster of interest + * @param[in] fs Information from the FAT boot sectors (bits per FAT entry) + */ +void get_fat(FAT_ENTRY * entry, void *fat, unsigned long cluster, DOS_FS * fs) +{ + unsigned char *ptr; + + switch (fs->fat_bits) { + case 12: + ptr = &((unsigned char *)fat)[cluster * 3 / 2]; + entry->value = 0xfff & (cluster & 1 ? (ptr[0] >> 4) | (ptr[1] << 4) : + (ptr[0] | ptr[1] << 8)); + break; + case 16: + entry->value = CF_LE_W(((unsigned short *)fat)[cluster]); + break; + case 32: + /* According to M$, the high 4 bits of a FAT32 entry are reserved and + * are not part of the cluster number. So we cut them off. */ + { + unsigned long e = CF_LE_L(((unsigned int *)fat)[cluster]); + entry->value = e & 0xfffffff; + entry->reserved = e >> 28; + } + break; + default: + die("Bad FAT entry size: %d bits.", fs->fat_bits); + } +} + +/** + * Build a bookkeeping structure from the partition's FAT table. + * If the partition has multiple FATs and they don't agree, try to pick a winner, + * and queue a command to overwrite the loser. + * One error that is fixed here is a cluster that links to something out of range. + * + * @param[inout] fs Information about the filesystem + */ +void read_fat(DOS_FS * fs) +{ + int eff_size; + unsigned long i; + void *first, *second = NULL; + int first_ok, second_ok; + unsigned long total_num_clusters; + + /* Clean up from previous pass */ + if (fs->fat) + free(fs->fat); + if (fs->cluster_owner) + free(fs->cluster_owner); + fs->fat = NULL; + fs->cluster_owner = NULL; + + total_num_clusters = fs->clusters + 2UL; + eff_size = (total_num_clusters * fs->fat_bits + 7) / 8ULL; + first = alloc(eff_size); + fs_read(fs->fat_start, eff_size, first); + if (fs->nfats > 1) { + second = alloc(eff_size); + fs_read(fs->fat_start + fs->fat_size, eff_size, second); + } + if (second && memcmp(first, second, eff_size) != 0) { + FAT_ENTRY first_media, second_media; + get_fat(&first_media, first, 0, fs); + get_fat(&second_media, second, 0, fs); + first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs); + second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs); + if (first_ok && !second_ok) { + printf("FATs differ - using first FAT.\n"); + fs_write(fs->fat_start + fs->fat_size, eff_size, first); + } + if (!first_ok && second_ok) { + printf("FATs differ - using second FAT.\n"); + fs_write(fs->fat_start, eff_size, second); + memcpy(first, second, eff_size); + } + if (first_ok && second_ok) { + if (interactive) { + printf("FATs differ but appear to be intact. Use which FAT ?\n" + "1) Use first FAT\n2) Use second FAT\n"); + if (get_key("12", "?") == '1') { + fs_write(fs->fat_start + fs->fat_size, eff_size, first); + } else { + fs_write(fs->fat_start, eff_size, second); + memcpy(first, second, eff_size); + } + } else { + printf("FATs differ but appear to be intact. Using first " + "FAT.\n"); + fs_write(fs->fat_start + fs->fat_size, eff_size, first); + } + } + if (!first_ok && !second_ok) { + printf("Both FATs appear to be corrupt. Giving up.\n"); + exit(1); + } + } + if (second) { + free(second); + } + fs->fat = (unsigned char *)first; + + fs->cluster_owner = alloc(total_num_clusters * sizeof(DOS_FILE *)); + memset(fs->cluster_owner, 0, (total_num_clusters * sizeof(DOS_FILE *))); + + /* Truncate any cluster chains that link to something out of range */ + for (i = 2; i < fs->clusters + 2; i++) { + FAT_ENTRY curEntry; + get_fat(&curEntry, fs->fat, i, fs); + if (curEntry.value == 1) { + printf("Cluster %ld out of range (1). Setting to EOF.\n", i - 2); + set_fat(fs, i, -1); + } + if (curEntry.value >= fs->clusters + 2 && + (curEntry.value < FAT_MIN_BAD(fs))) { + printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n", + i - 2, curEntry.value, fs->clusters + 2 - 1); + set_fat(fs, i, -1); + } + } +} + +/** + * Update the FAT entry for a specified cluster + * (i.e., change the cluster it links to). + * Queue a command to write out this change. + * + * @param[in,out] fs Information about the filesystem + * @param[in] cluster Cluster to change + * @param[in] new Cluster to link to + * Special values: + * 0 == free cluster + * -1 == end-of-chain + * -2 == bad cluster + */ +void set_fat(DOS_FS * fs, unsigned long cluster, unsigned long new) +{ + unsigned char *data = NULL; + int size; + loff_t offs; + + if ((long)new == -1) + new = FAT_EOF(fs); + else if ((long)new == -2) + new = FAT_BAD(fs); + switch (fs->fat_bits) { + case 12: + data = fs->fat + cluster * 3 / 2; + offs = fs->fat_start + cluster * 3 / 2; + if (cluster & 1) { + FAT_ENTRY prevEntry; + get_fat(&prevEntry, fs->fat, cluster - 1, fs); + data[0] = ((new & 0xf) << 4) | (prevEntry.value >> 8); + data[1] = new >> 4; + } else { + FAT_ENTRY subseqEntry; + get_fat(&subseqEntry, fs->fat, cluster + 1, fs); + data[0] = new & 0xff; + data[1] = (new >> 8) | (cluster == fs->clusters - 1 ? 0 : + (0xff & subseqEntry.value) << 4); + } + size = 2; + break; + case 16: + data = fs->fat + cluster * 2; + offs = fs->fat_start + cluster * 2; + *(unsigned short *)data = CT_LE_W(new); + size = 2; + break; + case 32: + { + FAT_ENTRY curEntry; + get_fat(&curEntry, fs->fat, cluster, fs); + + data = fs->fat + cluster * 4; + offs = fs->fat_start + cluster * 4; + /* According to M$, the high 4 bits of a FAT32 entry are reserved and + * are not part of the cluster number. So we never touch them. */ + *(unsigned long *)data = CT_LE_L((new & 0xfffffff) | + (curEntry.reserved << 28)); + size = 4; + } + break; + default: + die("Bad FAT entry size: %d bits.", fs->fat_bits); + } + fs_write(offs, size, data); + if (fs->nfats > 1) { + fs_write(offs + fs->fat_size, size, data); + } +} + +int bad_cluster(DOS_FS * fs, unsigned long cluster) +{ + FAT_ENTRY curEntry; + get_fat(&curEntry, fs->fat, cluster, fs); + + return FAT_IS_BAD(fs, curEntry.value); +} + +/** + * Get the cluster to which the specified cluster is linked. + * If the linked cluster is marked bad, abort. + * + * @param[in] fs Information about the filesystem + * @param[in] cluster Cluster to follow + * + * @return -1 'cluster' is at the end of the chain + * @return Other values Next cluster in this chain + */ +unsigned long next_cluster(DOS_FS * fs, unsigned long cluster) +{ + unsigned long value; + FAT_ENTRY curEntry; + + get_fat(&curEntry, fs->fat, cluster, fs); + + value = curEntry.value; + if (FAT_IS_BAD(fs, value)) + die("Internal error: next_cluster on bad cluster"); + return FAT_IS_EOF(fs, value) ? -1 : value; +} + +loff_t cluster_start(DOS_FS * fs, unsigned long cluster) +{ + return fs->data_start + ((loff_t) cluster - + 2) * (unsigned long long)fs->cluster_size; +} + +/** + * Update internal bookkeeping to show that the specified cluster belongs + * to the specified dentry. + * + * @param[in,out] fs Information about the filesystem + * @param[in] cluster Cluster being assigned + * @param[in] owner Information on dentry that owns this cluster + * (may be NULL) + */ +void set_owner(DOS_FS * fs, unsigned long cluster, DOS_FILE * owner) +{ + if (fs->cluster_owner == NULL) + die("Internal error: attempt to set owner in non-existent table"); + + if (owner && fs->cluster_owner[cluster] + && (fs->cluster_owner[cluster] != owner)) + die("Internal error: attempt to change file owner"); + fs->cluster_owner[cluster] = owner; +} + +DOS_FILE *get_owner(DOS_FS * fs, unsigned long cluster) +{ + if (fs->cluster_owner == NULL) + return NULL; + else + return fs->cluster_owner[cluster]; +} + +void fix_bad(DOS_FS * fs) +{ + unsigned long i; + + if (verbose) + printf("Checking for bad clusters.\n"); + for (i = 2; i < fs->clusters + 2; i++) { + FAT_ENTRY curEntry; + get_fat(&curEntry, fs->fat, i, fs); + + if (!get_owner(fs, i) && !FAT_IS_BAD(fs, curEntry.value)) + if (!fs_test(cluster_start(fs, i), fs->cluster_size)) { + printf("Cluster %lu is unreadable.\n", i); + set_fat(fs, i, -2); + } + } +} + +void reclaim_free(DOS_FS * fs) +{ + int reclaimed; + unsigned long i; + + if (verbose) + printf("Checking for unused clusters.\n"); + reclaimed = 0; + for (i = 2; i < fs->clusters + 2; i++) { + FAT_ENTRY curEntry; + get_fat(&curEntry, fs->fat, i, fs); + + if (!get_owner(fs, i) && curEntry.value && + !FAT_IS_BAD(fs, curEntry.value)) { + set_fat(fs, i, 0); + reclaimed++; + } + } + if (reclaimed) + printf("Reclaimed %d unused cluster%s (%llu bytes).\n", reclaimed, + reclaimed == 1 ? "" : "s", + (unsigned long long)reclaimed * fs->cluster_size); +} + +/** + * Assign the specified owner to all orphan chains (except cycles). + * Break cross-links between orphan chains. + * + * @param[in,out] fs Information about the filesystem + * @param[in] owner dentry to be assigned ownership of orphans + * @param[in,out] num_refs For each orphan cluster [index], how many + * clusters link to it. + * @param[in] start_cluster Where to start scanning for orphans + */ +static void tag_free(DOS_FS * fs, DOS_FILE * owner, unsigned long *num_refs, + unsigned long start_cluster) +{ + int prev; + unsigned long i, walk; + + if (start_cluster == 0) + start_cluster = 2; + + for (i = start_cluster; i < fs->clusters + 2; i++) { + FAT_ENTRY curEntry; + get_fat(&curEntry, fs->fat, i, fs); + + /* If the current entry is the head of an un-owned chain... */ + if (curEntry.value && !FAT_IS_BAD(fs, curEntry.value) && + !get_owner(fs, i) && !num_refs[i]) { + prev = 0; + /* Walk the chain, claiming ownership as we go */ + for (walk = i; walk != -1; walk = next_cluster(fs, walk)) { + if (!get_owner(fs, walk)) { + set_owner(fs, walk, owner); + } else { + /* We've run into cross-links between orphaned chains, + * or a cycle with a tail. + * Terminate this orphan chain (break the link) + */ + set_fat(fs, prev, -1); + + /* This is not necessary because 'walk' is owned and thus + * will never become the head of a chain (the only case + * that would matter during reclaim to files). + * It's easier to decrement than to prove that it's + * unnecessary. + */ + num_refs[walk]--; + break; + } + prev = walk; + } + } + } +} + +/** + * Recover orphan chains to files, handling any cycles or cross-links. + * + * @param[in,out] fs Information about the filesystem + */ +void reclaim_file(DOS_FS * fs) +{ + DOS_FILE orphan; + int reclaimed, files; + int changed = 0; + unsigned long i, next, walk; + unsigned long *num_refs = NULL; /* Only for orphaned clusters */ + unsigned long total_num_clusters; + + if (verbose) + printf("Reclaiming unconnected clusters.\n"); + + total_num_clusters = fs->clusters + 2UL; + num_refs = alloc(total_num_clusters * sizeof(unsigned long)); + memset(num_refs, 0, (total_num_clusters * sizeof(unsigned long))); + + /* Guarantee that all orphan chains (except cycles) end cleanly + * with an end-of-chain mark. + */ + + for (i = 2; i < total_num_clusters; i++) { + FAT_ENTRY curEntry; + get_fat(&curEntry, fs->fat, i, fs); + + next = curEntry.value; + if (!get_owner(fs, i) && next && next < fs->clusters + 2) { + /* Cluster is linked, but not owned (orphan) */ + FAT_ENTRY nextEntry; + get_fat(&nextEntry, fs->fat, next, fs); + + /* Mark it end-of-chain if it links into an owned cluster, + * a free cluster, or a bad cluster. + */ + if (get_owner(fs, next) || !nextEntry.value || + FAT_IS_BAD(fs, nextEntry.value)) + set_fat(fs, i, -1); + else + num_refs[next]++; + } + } + + /* Scan until all the orphans are accounted for, + * and all cycles and cross-links are broken + */ + do { + tag_free(fs, &orphan, num_refs, changed); + changed = 0; + + /* Any unaccounted-for orphans must be part of a cycle */ + for (i = 2; i < total_num_clusters; i++) { + FAT_ENTRY curEntry; + get_fat(&curEntry, fs->fat, i, fs); + + if (curEntry.value && !FAT_IS_BAD(fs, curEntry.value) && + !get_owner(fs, i)) { + if (!num_refs[curEntry.value]--) + die("Internal error: num_refs going below zero"); + set_fat(fs, i, -1); + changed = curEntry.value; + printf("Broke cycle at cluster %lu in free chain.\n", i); + + /* If we've created a new chain head, + * tag_free() can claim it + */ + if (num_refs[curEntry.value] == 0) + break; + } + } + } + while (changed); + + /* Now we can start recovery */ + files = reclaimed = 0; + for (i = 2; i < total_num_clusters; i++) + /* If this cluster is the head of an orphan chain... */ + if (get_owner(fs, i) == &orphan && !num_refs[i]) { + DIR_ENT de; + loff_t offset; + files++; + offset = alloc_rootdir_entry(fs, &de, "FSCK%04dREC"); + de.start = CT_LE_W(i & 0xffff); + if (fs->fat_bits == 32) + de.starthi = CT_LE_W(i >> 16); + for (walk = i; walk > 0 && walk != -1; + walk = next_cluster(fs, walk)) { + de.size = CT_LE_L(CF_LE_L(de.size) + fs->cluster_size); + reclaimed++; + } + fs_write(offset, sizeof(DIR_ENT), &de); + } + if (reclaimed) + printf("Reclaimed %d unused cluster%s (%llu bytes) in %d chain%s.\n", + reclaimed, reclaimed == 1 ? "" : "s", + (unsigned long long)reclaimed * fs->cluster_size, files, + files == 1 ? "" : "s"); + + free(num_refs); +} + +unsigned long update_free(DOS_FS * fs) +{ + unsigned long i; + unsigned long free = 0; + int do_set = 0; + + for (i = 2; i < fs->clusters + 2; i++) { + FAT_ENTRY curEntry; + get_fat(&curEntry, fs->fat, i, fs); + + if (!get_owner(fs, i) && !FAT_IS_BAD(fs, curEntry.value)) + ++free; + } + + if (!fs->fsinfo_start) + return free; + + if (verbose) + printf("Checking free cluster summary.\n"); + if (fs->free_clusters != 0xFFFFFFFF) { + if (free != fs->free_clusters) { + printf("Free cluster summary wrong (%ld vs. really %ld)\n", + fs->free_clusters, free); + if (interactive) + printf("1) Correct\n2) Don't correct\n"); + else + printf(" Auto-correcting.\n"); + if (!interactive || get_key("12", "?") == '1') + do_set = 1; + } + } else { + printf("Free cluster summary uninitialized (should be %ld)\n", free); + if (rw) { + if (interactive) + printf("1) Set it\n2) Leave it uninitialized\n"); + else + printf(" Auto-setting.\n"); + if (!interactive || get_key("12", "?") == '1') + do_set = 1; + } + } + + if (do_set) { + unsigned long le_free = CT_LE_L(free); + fs->free_clusters = free; + fs_write(fs->fsinfo_start + offsetof(struct info_sector, free_clusters), + sizeof(le_free), &le_free); + } + + return free; +} diff --git a/src/fat.h b/src/fat.h new file mode 100644 index 0000000..7eaef9e --- /dev/null +++ b/src/fat.h @@ -0,0 +1,85 @@ +/* fat.h - Read/write access to the FAT + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +#ifndef _FAT_H +#define _FAT_H + +void read_fat(DOS_FS * fs); + +/* Loads the FAT of the file system described by FS. Initializes the FAT, + replaces broken FATs and rejects invalid cluster entries. */ + +void get_fat(FAT_ENTRY * entry, void *fat, unsigned long cluster, DOS_FS * fs); + +/* Retrieve the FAT entry (next chained cluster) for CLUSTER. */ + +void set_fat(DOS_FS * fs, unsigned long cluster, unsigned long new); + +/* Changes the value of the CLUSTERth cluster of the FAT of FS to NEW. Special + values of NEW are -1 (EOF, 0xff8 or 0xfff8) and -2 (bad sector, 0xff7 or + 0xfff7) */ + +int bad_cluster(DOS_FS * fs, unsigned long cluster); + +/* Returns a non-zero integer if the CLUSTERth cluster is marked as bad or zero + otherwise. */ + +unsigned long next_cluster(DOS_FS * fs, unsigned long cluster); + +/* Returns the number of the cluster following CLUSTER, or -1 if this is the + last cluster of the respective cluster chain. CLUSTER must not be a bad + cluster. */ + +loff_t cluster_start(DOS_FS * fs, unsigned long cluster); + +/* Returns the byte offset of CLUSTER, relative to the respective device. */ + +void set_owner(DOS_FS * fs, unsigned long cluster, DOS_FILE * owner); + +/* Sets the owner pointer of the respective cluster to OWNER. If OWNER was NULL + before, it can be set to NULL or any non-NULL value. Otherwise, only NULL is + accepted as the new value. */ + +DOS_FILE *get_owner(DOS_FS * fs, unsigned long cluster); + +/* Returns the owner of the repective cluster or NULL if the cluster has no + owner. */ + +void fix_bad(DOS_FS * fs); + +/* Scans the disk for currently unused bad clusters and marks them as bad. */ + +void reclaim_free(DOS_FS * fs); + +/* Marks all allocated, but unused clusters as free. */ + +void reclaim_file(DOS_FS * fs); + +/* Scans the FAT for chains of allocated, but unused clusters and creates files + for them in the root directory. Also tries to fix all inconsistencies (e.g. + loops, shared clusters, etc.) in the process. */ + +unsigned long update_free(DOS_FS * fs); + +/* Updates free cluster count in FSINFO sector. */ + +#endif diff --git a/src/file.c b/src/file.c new file mode 100644 index 0000000..1272e17 --- /dev/null +++ b/src/file.c @@ -0,0 +1,286 @@ +/* file.c - Additional file attributes + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek */ + +#include +#include +#include +#include +#include + +#define _LINUX_STAT_H /* hack to avoid inclusion of */ +#define _LINUX_STRING_H_ /* hack to avoid inclusion of */ +#define _LINUX_FS_H /* hack to avoid inclusion of */ + +#include + +#include + +#include "common.h" +#include "file.h" +#include "charconv.h" + +FDSC *fp_root = NULL; + +static void put_char(char **p, unsigned char c) +{ + if (dos_char_to_printable(p, c)) + return; + if ((c >= ' ' && c < 0x7f) || c >= 0xa0) + *(*p)++ = c; + else { + *(*p)++ = '\\'; + *(*p)++ = '0' + (c >> 6); + *(*p)++ = '0' + ((c >> 3) & 7); + *(*p)++ = '0' + (c & 7); + } +} + +/** + * Construct the "pretty-printed" representation of the name in a short directory entry. + * + * @param[in] fixed Pointer to name[0] of a DIR_ENT + * + * @return Pointer to static string containing pretty "8.3" equivalent of the + * name in the directory entry. + */ +char *file_name(unsigned char *fixed) +{ + static char path[MSDOS_NAME * 4 + 2]; + char *p; + int i, j; + + p = path; + for (i = j = 0; i < 8; i++) + if (fixed[i] != ' ') { + while (j++ < i) + *p++ = ' '; + put_char(&p, fixed[i]); + } + if (strncmp((const char *)(fixed + 8), " ", 3)) { + *p++ = '.'; + for (i = j = 0; i < 3; i++) + if (fixed[i + 8] != ' ') { + while (j++ < i) + *p++ = ' '; + put_char(&p, fixed[i + 8]); + } + } + *p = 0; + return path; +} + +int file_cvt(unsigned char *name, unsigned char *fixed) +{ + unsigned char c; + int size, ext, cnt; + + size = 8; + ext = 0; + while (*name) { + c = *name; + if (c < ' ' || c > 0x7e || strchr("*?<>|\"/", c)) { + printf("Invalid character in name. Use \\ooo for special " + "characters.\n"); + return 0; + } + if (c == '.') { + if (ext) { + printf("Duplicate dots in name.\n"); + return 0; + } + while (size--) + *fixed++ = ' '; + size = 3; + ext = 1; + name++; + continue; + } + if (c == '\\') { + c = 0; + for (cnt = 3; cnt; cnt--) { + if (*name < '0' || *name > '7') { + printf("Invalid octal character.\n"); + return 0; + } + c = c * 8 + *name++ - '0'; + } + if (cnt < 4) { + printf("Expected three octal digits.\n"); + return 0; + } + name += 3; + } + if (islower(c)) + c = toupper(c); + if (size) { + *fixed++ = c; + size--; + } + name++; + } + if (*name || size == 8) + return 0; + if (!ext) { + while (size--) + *fixed++ = ' '; + size = 3; + } + while (size--) + *fixed++ = ' '; + return 1; +} + +void file_add(char *path, FD_TYPE type) +{ + FDSC **current, *walk; + char name[MSDOS_NAME]; + char *here; + + current = &fp_root; + if (*path != '/') + die("%s: Absolute path required.", path); + path++; + while (1) { + if ((here = strchr(path, '/'))) + *here = 0; + if (!file_cvt((unsigned char *)path, (unsigned char *)name)) + exit(2); + for (walk = *current; walk; walk = walk->next) + if (!here && (!strncmp(name, walk->name, MSDOS_NAME) || (type == + fdt_undelete + && + !strncmp + (name + 1, + walk->name + + 1, + MSDOS_NAME + - 1)))) + die("Ambiguous name: \"%s\"", path); + else if (here && !strncmp(name, walk->name, MSDOS_NAME)) + break; + if (!walk) { + walk = alloc(sizeof(FDSC)); + strncpy(walk->name, name, MSDOS_NAME); + walk->type = here ? fdt_none : type; + walk->first = NULL; + walk->next = *current; + *current = walk; + } + current = &walk->first; + if (!here) + break; + *here = '/'; + path = here + 1; + } +} + +FDSC **file_cd(FDSC ** curr, char *fixed) +{ + FDSC **walk; + + if (!curr || !*curr) + return NULL; + for (walk = curr; *walk; walk = &(*walk)->next) + if (!strncmp((*walk)->name, fixed, MSDOS_NAME) && (*walk)->first) + return &(*walk)->first; + return NULL; +} + +static FDSC **file_find(FDSC ** dir, char *fixed) +{ + if (!dir || !*dir) + return NULL; + if (*(unsigned char *)fixed == DELETED_FLAG) { + while (*dir) { + if (!strncmp((*dir)->name + 1, fixed + 1, MSDOS_NAME - 1) + && !(*dir)->first) + return dir; + dir = &(*dir)->next; + } + return NULL; + } + while (*dir) { + if (!strncmp((*dir)->name, fixed, MSDOS_NAME) && !(*dir)->first) + return dir; + dir = &(*dir)->next; + } + return NULL; +} + +/* Returns the attribute of the file FIXED in directory CURR or FDT_NONE if no + such file exists or if CURR is NULL. */ +FD_TYPE file_type(FDSC ** curr, char *fixed) +{ + FDSC **this; + + if ((this = file_find(curr, fixed))) + return (*this)->type; + return fdt_none; +} + +void file_modify(FDSC ** curr, char *fixed) +{ + FDSC **this, *next; + + if (!(this = file_find(curr, fixed))) + die("Internal error: file_find failed"); + switch ((*this)->type) { + case fdt_drop: + printf("Dropping %s\n", file_name((unsigned char *)fixed)); + *(unsigned char *)fixed = DELETED_FLAG; + break; + case fdt_undelete: + *fixed = *(*this)->name; + printf("Undeleting %s\n", file_name((unsigned char *)fixed)); + break; + default: + die("Internal error: file_modify"); + } + next = (*this)->next; + free(*this); + *this = next; +} + +static void report_unused(FDSC * this) +{ + FDSC *next; + + while (this) { + next = this->next; + if (this->first) + report_unused(this->first); + else if (this->type != fdt_none) + printf("Warning: did not %s file %s\n", this->type == fdt_drop ? + "drop" : "undelete", file_name((unsigned char *)this->name)); + free(this); + this = next; + } +} + +void file_unused(void) +{ + report_unused(fp_root); +} diff --git a/src/file.h b/src/file.h new file mode 100644 index 0000000..c7e2e4d --- /dev/null +++ b/src/file.h @@ -0,0 +1,70 @@ +/* file.h - Additional file attributes + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +#ifndef _FILE_H +#define _FILE_H + +typedef enum { fdt_none, fdt_drop, fdt_undelete } FD_TYPE; + +typedef struct _fptr { + char name[MSDOS_NAME]; + FD_TYPE type; + struct _fptr *first; /* first entry */ + struct _fptr *next; /* next file in directory */ +} FDSC; + +extern FDSC *fp_root; + +char *file_name(unsigned char *fixed); + +/* Returns a pointer to a pretty-printed representation of a fixed MS-DOS file + name. */ + +int file_cvt(unsigned char *name, unsigned char *fixed); + +/* Converts a pretty-printed file name to the fixed MS-DOS format. Returns a + non-zero integer on success, zero on failure. */ + +void file_add(char *path, FD_TYPE type); + +/* Define special attributes for a path. TYPE can be either FDT_DROP or + FDT_UNDELETE. */ + +FDSC **file_cd(FDSC ** curr, char *fixed); + +/* Returns a pointer to the directory descriptor of the subdirectory FIXED of + CURR, or NULL if no such subdirectory exists. */ + +FD_TYPE file_type(FDSC ** curr, char *fixed); + +/* Returns the attribute of the file FIXED in directory CURR or FDT_NONE if no + such file exists or if CURR is NULL. */ + +void file_modify(FDSC ** curr, char *fixed); + +/* Performs the necessary operation on the entry of CURR that is named FIXED. */ + +void file_unused(void); + +/* Displays warnings for all unused file attributes. */ + +#endif diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..d862fcf --- /dev/null +++ b/src/io.c @@ -0,0 +1,231 @@ +/* io.c - Virtual disk input/output + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* + * Thu Feb 26 01:15:36 CET 1998: Martin Schulze + * Fixed nasty bug that caused every file with a name like + * xxxxxxxx.xxx to be treated as bad name that needed to be fixed. + */ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek */ + +#define _LARGEFILE64_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dosfsck.h" +#include "common.h" +#include "io.h" + +typedef struct _change { + void *data; + loff_t pos; + int size; + struct _change *next; +} CHANGE; + +static CHANGE *changes, *last; +static int fd, did_change = 0; + +unsigned device_no; + +#ifdef __DJGPP__ +#include "volume.h" /* DOS lowlevel disk access functions */ +loff_t llseek(int fd, loff_t offset, int whence) +{ + if ((whence != SEEK_SET) || (fd == 4711)) + return -1; /* only those supported */ + return VolumeSeek(offset); +} + +#define open OpenVolume +#define close CloseVolume +#define read(a,b,c) ReadVolume(b,c) +#define write(a,b,c) WriteVolume(b,c) +#else +loff_t llseek(int fd, loff_t offset, int whence) +{ + return (loff_t) lseek64(fd, (off64_t) offset, whence); +} +#endif + +void fs_open(char *path, int rw) +{ + struct stat stbuf; + + if ((fd = open(path, rw ? O_RDWR : O_RDONLY)) < 0) { + perror("open"); + exit(6); + } + changes = last = NULL; + did_change = 0; + +#ifndef _DJGPP_ + if (fstat(fd, &stbuf) < 0) + pdie("fstat %s", path); + device_no = S_ISBLK(stbuf.st_mode) ? (stbuf.st_rdev >> 8) & 0xff : 0; +#else + if (IsWorkingOnImageFile()) { + if (fstat(GetVolumeHandle(), &stbuf) < 0) + pdie("fstat image %s", path); + device_no = 0; + } else { + /* return 2 for floppy, 1 for ramdisk, 7 for loopback */ + /* used by boot.c in Atari mode: floppy always FAT12, */ + /* loopback / ramdisk only FAT12 if usual floppy size, */ + /* harddisk always FAT16 on Atari... */ + device_no = (GetVolumeHandle() < 2) ? 2 : 1; + /* telling "floppy" for A:/B:, "ramdisk" for the rest */ + } +#endif +} + +/** + * Read data from the partition, accounting for any pending updates that are + * queued for writing. + * + * @param[in] pos Byte offset, relative to the beginning of the partition, + * at which to read + * @param[in] size Number of bytes to read + * @param[out] data Where to put the data read + */ +void fs_read(loff_t pos, int size, void *data) +{ + CHANGE *walk; + int got; + + if (llseek(fd, pos, 0) != pos) + pdie("Seek to %lld", pos); + if ((got = read(fd, data, size)) < 0) + pdie("Read %d bytes at %lld", size, pos); + if (got != size) + die("Got %d bytes instead of %d at %lld", got, size, pos); + for (walk = changes; walk; walk = walk->next) { + if (walk->pos < pos + size && walk->pos + walk->size > pos) { + if (walk->pos < pos) + memcpy(data, (char *)walk->data + pos - walk->pos, min(size, + walk->size + - pos + + walk->pos)); + else + memcpy((char *)data + walk->pos - pos, walk->data, + min(walk->size, size + pos - walk->pos)); + } + } +} + +int fs_test(loff_t pos, int size) +{ + void *scratch; + int okay; + + if (llseek(fd, pos, 0) != pos) + pdie("Seek to %lld", pos); + scratch = alloc(size); + okay = read(fd, scratch, size) == size; + free(scratch); + return okay; +} + +void fs_write(loff_t pos, int size, void *data) +{ + CHANGE *new; + int did; + + if (write_immed) { + did_change = 1; + if (llseek(fd, pos, 0) != pos) + pdie("Seek to %lld", pos); + if ((did = write(fd, data, size)) == size) + return; + if (did < 0) + pdie("Write %d bytes at %lld", size, pos); + die("Wrote %d bytes instead of %d at %lld", did, size, pos); + } + new = alloc(sizeof(CHANGE)); + new->pos = pos; + memcpy(new->data = alloc(new->size = size), data, size); + new->next = NULL; + if (last) + last->next = new; + else + changes = new; + last = new; +} + +static void fs_flush(void) +{ + CHANGE *this; + int size; + + while (changes) { + this = changes; + changes = changes->next; + if (llseek(fd, this->pos, 0) != this->pos) + fprintf(stderr, + "Seek to %lld failed: %s\n Did not write %d bytes.\n", + (long long)this->pos, strerror(errno), this->size); + else if ((size = write(fd, this->data, this->size)) < 0) + fprintf(stderr, "Writing %d bytes at %lld failed: %s\n", this->size, + (long long)this->pos, strerror(errno)); + else if (size != this->size) + fprintf(stderr, "Wrote %d bytes instead of %d bytes at %lld." + "\n", size, this->size, (long long)this->pos); + free(this->data); + free(this); + } +} + +int fs_close(int write) +{ + CHANGE *next; + int changed; + + changed = ! !changes; + if (write) + fs_flush(); + else + while (changes) { + next = changes->next; + free(changes->data); + free(changes); + changes = next; + } + if (close(fd) < 0) + pdie("closing file system"); + return changed || did_change; +} + +int fs_changed(void) +{ + return ! !changes || did_change; +} diff --git a/src/io.h b/src/io.h new file mode 100644 index 0000000..45cb7d8 --- /dev/null +++ b/src/io.h @@ -0,0 +1,71 @@ +/* io.h - Virtual disk input/output + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek */ + +#ifndef _IO_H +#define _IO_H + +#include /* for loff_t */ + +loff_t llseek(int fd, loff_t offset, int whence); + +/* lseek() analogue for large offsets. */ + +void fs_open(char *path, int rw); + +/* Opens the file system PATH. If RW is zero, the file system is opened + read-only, otherwise, it is opened read-write. */ + +void fs_read(loff_t pos, int size, void *data); + +/* Reads SIZE bytes starting at POS into DATA. Performs all applicable + changes. */ + +int fs_test(loff_t pos, int size); + +/* Returns a non-zero integer if SIZE bytes starting at POS can be read without + errors. Otherwise, it returns zero. */ + +void fs_write(loff_t pos, int size, void *data); + +/* If write_immed is non-zero, SIZE bytes are written from DATA to the disk, + starting at POS. If write_immed is zero, the change is added to a list in + memory. */ + +int fs_close(int write); + +/* Closes the file system, performs all pending changes if WRITE is non-zero + and removes the list of changes. Returns a non-zero integer if the file + system has been changed since the last fs_open, zero otherwise. */ + +int fs_changed(void); + +/* Determines whether the file system has changed. See fs_close. */ + +extern unsigned device_no; + +/* Major number of device (0 if file) and size (in 512 byte sectors) */ + +#endif diff --git a/src/lfn.c b/src/lfn.c new file mode 100644 index 0000000..4c4ec3e --- /dev/null +++ b/src/lfn.c @@ -0,0 +1,524 @@ +/* lfn.c - Functions for handling VFAT long filenames + + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "io.h" +#include "dosfsck.h" +#include "lfn.h" +#include "file.h" + +typedef struct { + __u8 id; /* sequence number for slot */ + __u8 name0_4[10]; /* first 5 characters in name */ + __u8 attr; /* attribute byte */ + __u8 reserved; /* always 0 */ + __u8 alias_checksum; /* checksum for 8.3 alias */ + __u8 name5_10[12]; /* 6 more characters in name */ + __u16 start; /* starting cluster number, 0 in long slots */ + __u8 name11_12[4]; /* last 2 characters in name */ +} LFN_ENT; + +#define LFN_ID_START 0x40 +#define LFN_ID_SLOTMASK 0x1f + +#define CHARS_PER_LFN 13 + +/* These modul-global vars represent the state of the LFN parser */ +unsigned char *lfn_unicode = NULL; +unsigned char lfn_checksum; +int lfn_slot = -1; +loff_t *lfn_offsets = NULL; +int lfn_parts = 0; + +static unsigned char fat_uni2esc[64] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '+', '-' +}; + +/* This defines which unicode chars are directly convertable to ISO-8859-1 */ +#define UNICODE_CONVERTABLE(cl,ch) (ch == 0 && (cl < 0x80 || cl >= 0xa0)) + +/* for maxlen param */ +#define UNTIL_0 INT_MAX + +/* Convert name part in 'lfn' from unicode to ASCII */ +#define CNV_THIS_PART(lfn) \ + ({ \ + unsigned char __part_uni[CHARS_PER_LFN*2]; \ + copy_lfn_part( __part_uni, lfn ); \ + cnv_unicode( __part_uni, CHARS_PER_LFN, 0 ); \ + }) + +/* Convert name parts collected so far (from previous slots) from unicode to + * ASCII */ +#define CNV_PARTS_SO_FAR() \ + (cnv_unicode( lfn_unicode+(lfn_slot*CHARS_PER_LFN*2), \ + lfn_parts*CHARS_PER_LFN, 0 )) + +#define BYTES_TO_WCHAR(cl,ch) ((wchar_t)((unsigned)(cl) + ((unsigned)(ch) << 8))) +static size_t mbslen(wchar_t x) +{ + wchar_t wstr[] = { x, 0 }; + return wcstombs(NULL, wstr, 0); +} + +static size_t wctombs(char *dest, wchar_t x) +{ + wchar_t wstr[] = { x, 0 }; + size_t size = wcstombs(NULL, wstr, 0); + if (size != (size_t) - 1) + size = wcstombs(dest, wstr, size + 1); + return size; +} + +/* This function converts an unicode string to a normal ASCII string, assuming + * ISO-8859-1 charset. Characters not in 8859-1 are converted to the same + * escape notation as used by the kernel, i.e. the uuencode-like ":xxx" */ +static char *cnv_unicode(const unsigned char *uni, int maxlen, int use_q) +{ + const unsigned char *up; + unsigned char *out, *cp; + int len, val; + size_t x; + + for (len = 0, up = uni; (up - uni) / 2 < maxlen && (up[0] || up[1]); + up += 2) { + if ((x = mbslen(BYTES_TO_WCHAR(up[0], up[1]))) != (size_t) - 1) + len += x; + else if (UNICODE_CONVERTABLE(up[0], up[1])) + ++len; + else + len += 4; + } + cp = out = use_q ? qalloc(&mem_queue, len + 1) : alloc(len + 1); + + for (up = uni; (up - uni) / 2 < maxlen && (up[0] || up[1]); up += 2) { + if ((x = wctombs(cp, BYTES_TO_WCHAR(up[0], up[1]))) != (size_t) - 1) + cp += x; + else if (UNICODE_CONVERTABLE(up[0], up[1])) + *cp++ = up[0]; + else { + /* here the same escape notation is used as in the Linux kernel */ + *cp++ = ':'; + val = (up[1] << 8) + up[0]; + cp[2] = fat_uni2esc[val & 0x3f]; + val >>= 6; + cp[1] = fat_uni2esc[val & 0x3f]; + val >>= 6; + cp[0] = fat_uni2esc[val & 0x3f]; + cp += 3; + } + } + *cp = 0; + + return (char *)out; +} + +static void copy_lfn_part(unsigned char *dst, LFN_ENT * lfn) +{ + memcpy(dst, lfn->name0_4, 10); + memcpy(dst + 10, lfn->name5_10, 12); + memcpy(dst + 22, lfn->name11_12, 4); +} + +static void clear_lfn_slots(int start, int end) +{ + int i; + LFN_ENT empty; + + /* New dir entry is zeroed except first byte, which is set to 0xe5. + * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading + * a directory at the first zero entry... + */ + memset(&empty, 0, sizeof(empty)); + empty.id = DELETED_FLAG; + + for (i = start; i <= end; ++i) { + fs_write(lfn_offsets[i], sizeof(LFN_ENT), &empty); + } +} + +void lfn_fix_checksum(loff_t from, loff_t to, const char *short_name) +{ + int i; + __u8 sum; + for (sum = 0, i = 0; i < 11; i++) + sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + short_name[i]; + + for (; from < to; from += sizeof(LFN_ENT)) { + fs_write(from + offsetof(LFN_ENT, alias_checksum), sizeof(sum), &sum); + } +} + +void lfn_reset(void) +{ + if (lfn_unicode) + free(lfn_unicode); + lfn_unicode = NULL; + if (lfn_offsets) + free(lfn_offsets); + lfn_offsets = NULL; + lfn_slot = -1; +} + +/* This function is only called with de->attr == VFAT_LN_ATTR. It stores part + * of the long name. */ +void lfn_add_slot(DIR_ENT * de, loff_t dir_offset) +{ + LFN_ENT *lfn = (LFN_ENT *) de; + int slot = lfn->id & LFN_ID_SLOTMASK; + unsigned offset; + + if (lfn_slot == 0) + lfn_check_orphaned(); + + if (de->attr != VFAT_LN_ATTR) + die("lfn_add_slot called with non-LFN directory entry"); + + if (lfn->id & LFN_ID_START && slot != 0) { + if (lfn_slot != -1) { + int can_clear = 0; + /* There is already a LFN "in progess", so it is an error that a + * new start entry is here. */ + /* Causes: 1) if slot# == expected: start bit set mysteriously, 2) + * old LFN overwritten by new one */ + /* Fixes: 1) delete previous LFN 2) if slot# == expected and + * checksum ok: clear start bit */ + /* XXX: Should delay that until next LFN known (then can better + * display the name) */ + printf("A new long file name starts within an old one.\n"); + if (slot == lfn_slot && lfn->alias_checksum == lfn_checksum) { + char *part1 = CNV_THIS_PART(lfn); + char *part2 = CNV_PARTS_SO_FAR(); + printf(" It could be that the LFN start bit is wrong here\n" + " if \"%s\" seems to match \"%s\".\n", part1, part2); + free(part1); + free(part2); + can_clear = 1; + } + if (interactive) { + printf("1: Delete previous LFN\n2: Leave it as it is.\n"); + if (can_clear) + printf("3: Clear start bit and concatenate LFNs\n"); + } else + printf(" Not auto-correcting this.\n"); + if (interactive) { + switch (get_key(can_clear ? "123" : "12", "?")) { + case '1': + clear_lfn_slots(0, lfn_parts - 1); + lfn_reset(); + break; + case '2': + break; + case '3': + lfn->id &= ~LFN_ID_START; + fs_write(dir_offset + offsetof(LFN_ENT, id), + sizeof(lfn->id), &lfn->id); + break; + } + } + } + lfn_slot = slot; + lfn_checksum = lfn->alias_checksum; + lfn_unicode = alloc((lfn_slot * CHARS_PER_LFN + 1) * 2); + lfn_offsets = alloc(lfn_slot * sizeof(loff_t)); + lfn_parts = 0; + } else if (lfn_slot == -1 && slot != 0) { + /* No LFN in progress, but slot found; start bit missing */ + /* Causes: 1) start bit got lost, 2) Previous slot with start bit got + * lost */ + /* Fixes: 1) delete LFN, 2) set start bit */ + char *part = CNV_THIS_PART(lfn); + printf("Long filename fragment \"%s\" found outside a LFN " + "sequence.\n (Maybe the start bit is missing on the " + "last fragment)\n", part); + if (interactive) { + printf("1: Delete fragment\n2: Leave it as it is.\n" + "3: Set start bit\n"); + } else + printf(" Not auto-correcting this.\n"); + switch (interactive ? get_key("123", "?") : '2') { + case '1': + if (!lfn_offsets) + lfn_offsets = alloc(sizeof(loff_t)); + lfn_offsets[0] = dir_offset; + clear_lfn_slots(0, 0); + lfn_reset(); + return; + case '2': + lfn_reset(); + return; + case '3': + lfn->id |= LFN_ID_START; + fs_write(dir_offset + offsetof(LFN_ENT, id), + sizeof(lfn->id), &lfn->id); + lfn_slot = slot; + lfn_checksum = lfn->alias_checksum; + lfn_unicode = alloc((lfn_slot * CHARS_PER_LFN + 1) * 2); + lfn_offsets = alloc(lfn_slot * sizeof(loff_t)); + lfn_parts = 0; + break; + } + } else if (slot != lfn_slot) { + /* wrong sequence number */ + /* Causes: 1) seq-no destroyed */ + /* Fixes: 1) delete LFN, 2) fix number (maybe only if following parts + * are ok?, maybe only if checksum is ok?) (Attention: space + * for name was allocated before!) */ + int can_fix = 0; + printf("Unexpected long filename sequence number " + "(%d vs. expected %d).\n", slot, lfn_slot); + if (lfn->alias_checksum == lfn_checksum && lfn_slot > 0) { + char *part1 = CNV_THIS_PART(lfn); + char *part2 = CNV_PARTS_SO_FAR(); + printf(" It could be that just the number is wrong\n" + " if \"%s\" seems to match \"%s\".\n", part1, part2); + free(part1); + free(part2); + can_fix = 1; + } + if (interactive) { + printf + ("1: Delete LFN\n2: Leave it as it is (and ignore LFN so far)\n"); + if (can_fix) + printf("3: Correct sequence number\n"); + } else + printf(" Not auto-correcting this.\n"); + switch (interactive ? get_key(can_fix ? "123" : "12", "?") : '2') { + case '1': + if (!lfn_offsets) { + lfn_offsets = alloc(sizeof(loff_t)); + lfn_parts = 0; + } + lfn_offsets[lfn_parts++] = dir_offset; + clear_lfn_slots(0, lfn_parts - 1); + lfn_reset(); + return; + case '2': + lfn_reset(); + return; + case '3': + lfn->id = (lfn->id & ~LFN_ID_SLOTMASK) | lfn_slot; + fs_write(dir_offset + offsetof(LFN_ENT, id), + sizeof(lfn->id), &lfn->id); + break; + } + } + + if (lfn->alias_checksum != lfn_checksum) { + /* checksum mismatch */ + /* Causes: 1) checksum field here destroyed */ + /* Fixes: 1) delete LFN, 2) fix checksum */ + printf("Checksum in long filename part wrong " + "(%02x vs. expected %02x).\n", + lfn->alias_checksum, lfn_checksum); + if (interactive) { + printf("1: Delete LFN\n2: Leave it as it is.\n" + "3: Correct checksum\n"); + } else + printf(" Not auto-correcting this.\n"); + if (interactive) { + switch (get_key("123", "?")) { + case '1': + lfn_offsets[lfn_parts++] = dir_offset; + clear_lfn_slots(0, lfn_parts - 1); + lfn_reset(); + return; + case '2': + break; + case '3': + lfn->alias_checksum = lfn_checksum; + fs_write(dir_offset + offsetof(LFN_ENT, alias_checksum), + sizeof(lfn->alias_checksum), &lfn->alias_checksum); + break; + } + } + } + + if (lfn_slot != -1) { + lfn_slot--; + offset = lfn_slot * CHARS_PER_LFN * 2; + copy_lfn_part(lfn_unicode + offset, lfn); + if (lfn->id & LFN_ID_START) + lfn_unicode[offset + 26] = lfn_unicode[offset + 27] = 0; + lfn_offsets[lfn_parts++] = dir_offset; + } + + if (lfn->reserved != 0) { + printf("Reserved field in VFAT long filename slot is not 0 " + "(but 0x%02x).\n", lfn->reserved); + if (interactive) + printf("1: Fix.\n2: Leave it.\n"); + else + printf("Auto-setting to 0.\n"); + if (!interactive || get_key("12", "?") == '1') { + lfn->reserved = 0; + fs_write(dir_offset + offsetof(LFN_ENT, reserved), + sizeof(lfn->reserved), &lfn->reserved); + } + } + if (lfn->start != CT_LE_W(0)) { + printf("Start cluster field in VFAT long filename slot is not 0 " + "(but 0x%04x).\n", lfn->start); + if (interactive) + printf("1: Fix.\n2: Leave it.\n"); + else + printf("Auto-setting to 0.\n"); + if (!interactive || get_key("12", "?") == '1') { + lfn->start = CT_LE_W(0); + fs_write(dir_offset + offsetof(LFN_ENT, start), + sizeof(lfn->start), &lfn->start); + } + } +} + +/* This function is always called when de->attr != VFAT_LN_ATTR is found, to + * retrieve the previously constructed LFN. */ +char *lfn_get(DIR_ENT * de, loff_t * lfn_offset) +{ + char *lfn; + __u8 sum; + int i; + + *lfn_offset = 0; + if (de->attr == VFAT_LN_ATTR) + die("lfn_get called with LFN directory entry"); + +#if 0 + if (de->lcase) + printf("lcase=%02x\n", de->lcase); +#endif + + if (lfn_slot == -1) + /* no long name for this file */ + return NULL; + + if (lfn_slot != 0) { + /* The long name isn't finished yet. */ + /* Causes: 1) LFN slot overwritten by non-VFAT aware tool */ + /* Fixes: 1) delete LFN 2) move overwriting entry to somewhere else + * and let user enter missing part of LFN (hard to do :-() + * 3) renumber entries and truncate name */ + char *long_name = CNV_PARTS_SO_FAR(); + char *short_name = file_name(de->name); + printf("Unfinished long file name \"%s\".\n" + " (Start may have been overwritten by %s)\n", + long_name, short_name); + free(long_name); + if (interactive) { + printf("1: Delete LFN\n2: Leave it as it is.\n" + "3: Fix numbering (truncates long name and attaches " + "it to short name %s)\n", short_name); + } else + printf(" Not auto-correcting this.\n"); + switch (interactive ? get_key("123", "?") : '2') { + case '1': + clear_lfn_slots(0, lfn_parts - 1); + lfn_reset(); + return NULL; + case '2': + lfn_reset(); + return NULL; + case '3': + for (i = 0; i < lfn_parts; ++i) { + __u8 id = (lfn_parts - i) | (i == 0 ? LFN_ID_START : 0); + fs_write(lfn_offsets[i] + offsetof(LFN_ENT, id), + sizeof(id), &id); + } + memmove(lfn_unicode, lfn_unicode + lfn_slot * CHARS_PER_LFN * 2, + lfn_parts * CHARS_PER_LFN * 2); + break; + } + } + + for (sum = 0, i = 0; i < 11; i++) + sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + de->name[i]; + if (sum != lfn_checksum) { + /* checksum doesn't match, long name doesn't apply to this alias */ + /* Causes: 1) alias renamed */ + /* Fixes: 1) Fix checksum in LFN entries */ + char *long_name = CNV_PARTS_SO_FAR(); + char *short_name = file_name(de->name); + printf("Wrong checksum for long file name \"%s\".\n" + " (Short name %s may have changed without updating the long name)\n", + long_name, short_name); + free(long_name); + if (interactive) { + printf("1: Delete LFN\n2: Leave it as it is.\n" + "3: Fix checksum (attaches to short name %s)\n", short_name); + } else + printf(" Not auto-correcting this.\n"); + if (interactive) { + switch (get_key("123", "?")) { + case '1': + clear_lfn_slots(0, lfn_parts - 1); + lfn_reset(); + return NULL; + case '2': + lfn_reset(); + return NULL; + case '3': + for (i = 0; i < lfn_parts; ++i) { + fs_write(lfn_offsets[i] + offsetof(LFN_ENT, alias_checksum), + sizeof(sum), &sum); + } + break; + } + } + } + + *lfn_offset = lfn_offsets[0]; + lfn = cnv_unicode(lfn_unicode, UNTIL_0, 1); + lfn_reset(); + return (lfn); +} + +void lfn_check_orphaned(void) +{ + char *long_name; + + if (lfn_slot == -1) + return; + + long_name = CNV_PARTS_SO_FAR(); + printf("Orphaned long file name part \"%s\"\n", long_name); + if (interactive) + printf("1: Delete.\n2: Leave it.\n"); + else + printf(" Auto-deleting.\n"); + if (!interactive || get_key("12", "?") == '1') { + clear_lfn_slots(0, lfn_parts - 1); + } + lfn_reset(); +} diff --git a/src/lfn.h b/src/lfn.h new file mode 100644 index 0000000..41b08f3 --- /dev/null +++ b/src/lfn.h @@ -0,0 +1,39 @@ +/* lfn.h - Functions for handling VFAT long filenames + + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +#ifndef _LFN_H +#define _LFN_H + +void lfn_reset(void); +/* Reset the state of the LFN parser. */ + +void lfn_add_slot(DIR_ENT * de, loff_t dir_offset); +/* Process a dir slot that is a VFAT LFN entry. */ + +char *lfn_get(DIR_ENT * de, loff_t * lfn_offset); +/* Retrieve the long name for the proper dir entry. */ + +void lfn_check_orphaned(void); + +void lfn_fix_checksum(loff_t from, loff_t to, const char *short_name); + +#endif diff --git a/src/mkdosfs.c b/src/mkdosfs.c new file mode 100644 index 0000000..d7c94f5 --- /dev/null +++ b/src/mkdosfs.c @@ -0,0 +1,1734 @@ +/* mkdosfs.c - utility to create FAT/MS-DOS filesystems + + Copyright (C) 1991 Linus Torvalds + Copyright (C) 1992-1993 Remy Card + Copyright (C) 1993-1994 David Hudson + Copyright (C) 1998 H. Peter Anvin + Copyright (C) 1998-2005 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* Description: Utility to allow an MS-DOS filesystem to be created + under Linux. A lot of the basic structure of this program has been + borrowed from Remy Card's "mke2fs" code. + + As far as possible the aim here is to make the "mkdosfs" command + look almost identical to the other Linux filesystem make utilties, + eg bad blocks are still specified as blocks, not sectors, but when + it comes down to it, DOS is tied to the idea of a sector (512 bytes + as a rule), and not the block. For example the boot block does not + occupy a full cluster. + + Fixes/additions May 1998 by Roman Hodek + : + - Atari format support + - New options -A, -S, -C + - Support for filesystems > 2GB + - FAT32 support */ + +/* Include the header files */ + +#include "version.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if __BYTE_ORDER == __BIG_ENDIAN + +#include +#ifdef __le16_to_cpu +/* ++roman: 2.1 kernel headers define these function, they're probably more + * efficient then coding the swaps machine-independently. */ +#define CF_LE_W __le16_to_cpu +#define CF_LE_L __le32_to_cpu +#define CT_LE_W __cpu_to_le16 +#define CT_LE_L __cpu_to_le32 +#else +#define CF_LE_W(v) ((((v) & 0xff) << 8) | (((v) >> 8) & 0xff)) +#define CF_LE_L(v) (((unsigned)(v)>>24) | (((unsigned)(v)>>8)&0xff00) | \ + (((unsigned)(v)<<8)&0xff0000) | ((unsigned)(v)<<24)) +#define CT_LE_W(v) CF_LE_W(v) +#define CT_LE_L(v) CF_LE_L(v) +#endif /* defined(__le16_to_cpu) */ + +#else + +#define CF_LE_W(v) (v) +#define CF_LE_L(v) (v) +#define CT_LE_W(v) (v) +#define CT_LE_L(v) (v) + +#endif /* __BIG_ENDIAN */ + +/* In earlier versions, an own llseek() was used, but glibc lseek() is + * sufficient (or even better :) for 64 bit offsets in the meantime */ +#define llseek lseek + +/* Constant definitions */ + +#define TRUE 1 /* Boolean constants */ +#define FALSE 0 + +#define TEST_BUFFER_BLOCKS 16 +#define HARD_SECTOR_SIZE 512 +#define SECTORS_PER_BLOCK ( BLOCK_SIZE / HARD_SECTOR_SIZE ) + +#define NO_NAME "NO NAME " + +/* Macro definitions */ + +/* Report a failure message and return a failure error code */ + +#define die( str ) fatal_error( "%s: " str "\n" ) + +/* Mark a cluster in the FAT as bad */ + +#define mark_sector_bad( sector ) mark_FAT_sector( sector, FAT_BAD ) + +/* Compute ceil(a/b) */ + +inline int cdiv(int a, int b) +{ + return (a + b - 1) / b; +} + +/* MS-DOS filesystem structures -- I included them here instead of + including linux/msdos_fs.h since that doesn't include some fields we + need */ + +#define ATTR_RO 1 /* read-only */ +#define ATTR_HIDDEN 2 /* hidden */ +#define ATTR_SYS 4 /* system */ +#define ATTR_VOLUME 8 /* volume label */ +#define ATTR_DIR 16 /* directory */ +#define ATTR_ARCH 32 /* archived */ + +#define ATTR_NONE 0 /* no attribute bits */ +#define ATTR_UNUSED (ATTR_VOLUME | ATTR_ARCH | ATTR_SYS | ATTR_HIDDEN) + /* attribute bits that are copied "as is" */ + +/* FAT values */ +#define FAT_EOF (atari_format ? 0x0fffffff : 0x0ffffff8) +#define FAT_BAD 0x0ffffff7 + +#define MSDOS_EXT_SIGN 0x29 /* extended boot sector signature */ +#define MSDOS_FAT12_SIGN "FAT12 " /* FAT12 filesystem signature */ +#define MSDOS_FAT16_SIGN "FAT16 " /* FAT16 filesystem signature */ +#define MSDOS_FAT32_SIGN "FAT32 " /* FAT32 filesystem signature */ + +#define BOOT_SIGN 0xAA55 /* Boot sector magic number */ + +#define MAX_CLUST_12 ((1 << 12) - 16) +#define MAX_CLUST_16 ((1 << 16) - 16) +#define MIN_CLUST_32 65529 +/* M$ says the high 4 bits of a FAT32 FAT entry are reserved and don't belong + * to the cluster number. So the max. cluster# is based on 2^28 */ +#define MAX_CLUST_32 ((1 << 28) - 16) + +#define FAT12_THRESHOLD 4085 + +#define OLDGEMDOS_MAX_SECTORS 32765 +#define GEMDOS_MAX_SECTORS 65531 +#define GEMDOS_MAX_SECTOR_SIZE (16*1024) + +#define BOOTCODE_SIZE 448 +#define BOOTCODE_FAT32_SIZE 420 + +/* __attribute__ ((packed)) is used on all structures to make gcc ignore any + * alignments */ + +struct msdos_volume_info { + __u8 drive_number; /* BIOS drive number */ + __u8 RESERVED; /* Unused */ + __u8 ext_boot_sign; /* 0x29 if fields below exist (DOS 3.3+) */ + __u8 volume_id[4]; /* Volume ID number */ + __u8 volume_label[11]; /* Volume label */ + __u8 fs_type[8]; /* Typically FAT12 or FAT16 */ +} __attribute__ ((packed)); + +struct msdos_boot_sector { + __u8 boot_jump[3]; /* Boot strap short or near jump */ + __u8 system_id[8]; /* Name - can be used to special case + partition manager volumes */ + __u8 sector_size[2]; /* bytes per logical sector */ + __u8 cluster_size; /* sectors/cluster */ + __u16 reserved; /* reserved sectors */ + __u8 fats; /* number of FATs */ + __u8 dir_entries[2]; /* root directory entries */ + __u8 sectors[2]; /* number of sectors */ + __u8 media; /* media code (unused) */ + __u16 fat_length; /* sectors/FAT */ + __u16 secs_track; /* sectors per track */ + __u16 heads; /* number of heads */ + __u32 hidden; /* hidden sectors (unused) */ + __u32 total_sect; /* number of sectors (if sectors == 0) */ + union { + struct { + struct msdos_volume_info vi; + __u8 boot_code[BOOTCODE_SIZE]; + } __attribute__ ((packed)) _oldfat; + struct { + __u32 fat32_length; /* sectors/FAT */ + __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ + __u8 version[2]; /* major, minor filesystem version */ + __u32 root_cluster; /* first cluster in root directory */ + __u16 info_sector; /* filesystem info sector */ + __u16 backup_boot; /* backup boot sector */ + __u16 reserved2[6]; /* Unused */ + struct msdos_volume_info vi; + __u8 boot_code[BOOTCODE_FAT32_SIZE]; + } __attribute__ ((packed)) _fat32; + } __attribute__ ((packed)) fstype; + __u16 boot_sign; +} __attribute__ ((packed)); +#define fat32 fstype._fat32 +#define oldfat fstype._oldfat + +struct fat32_fsinfo { + __u32 reserved1; /* Nothing as far as I can tell */ + __u32 signature; /* 0x61417272L */ + __u32 free_clusters; /* Free cluster count. -1 if unknown */ + __u32 next_cluster; /* Most recently allocated cluster. + * Unused under Linux. */ + __u32 reserved2[4]; +}; + +struct msdos_dir_entry { + char name[8], ext[3]; /* name and extension */ + __u8 attr; /* attribute bits */ + __u8 lcase; /* Case for base and extension */ + __u8 ctime_ms; /* Creation time, milliseconds */ + __u16 ctime; /* Creation time */ + __u16 cdate; /* Creation date */ + __u16 adate; /* Last access date */ + __u16 starthi; /* high 16 bits of first cl. (FAT32) */ + __u16 time, date, start; /* time, date and first cluster */ + __u32 size; /* file size (in bytes) */ +} __attribute__ ((packed)); + +/* The "boot code" we put into the filesystem... it writes a message and + tells the user to try again */ + +char dummy_boot_jump[3] = { 0xeb, 0x3c, 0x90 }; + +char dummy_boot_jump_m68k[2] = { 0x60, 0x1c }; + +#define MSG_OFFSET_OFFSET 3 +char dummy_boot_code[BOOTCODE_SIZE] = "\x0e" /* push cs */ + "\x1f" /* pop ds */ + "\xbe\x5b\x7c" /* mov si, offset message_txt */ + /* write_msg: */ + "\xac" /* lodsb */ + "\x22\xc0" /* and al, al */ + "\x74\x0b" /* jz key_press */ + "\x56" /* push si */ + "\xb4\x0e" /* mov ah, 0eh */ + "\xbb\x07\x00" /* mov bx, 0007h */ + "\xcd\x10" /* int 10h */ + "\x5e" /* pop si */ + "\xeb\xf0" /* jmp write_msg */ + /* key_press: */ + "\x32\xe4" /* xor ah, ah */ + "\xcd\x16" /* int 16h */ + "\xcd\x19" /* int 19h */ + "\xeb\xfe" /* foo: jmp foo */ + /* message_txt: */ + "This is not a bootable disk. Please insert a bootable floppy and\r\n" + "press any key to try again ... \r\n"; + +#define MESSAGE_OFFSET 29 /* Offset of message in above code */ + +/* Global variables - the root of all evil :-) - see these and weep! */ + +static char *program_name = "mkdosfs"; /* Name of the program */ +static char *device_name = NULL; /* Name of the device on which to create the filesystem */ +static int atari_format = 0; /* Use Atari variation of MS-DOS FS format */ +static int check = FALSE; /* Default to no readablity checking */ +static int verbose = 0; /* Default to verbose mode off */ +static long volume_id; /* Volume ID number */ +static time_t create_time; /* Creation time */ +static struct timeval create_timeval; /* Creation time */ +static char volume_name[] = NO_NAME; /* Volume name */ +static unsigned long long blocks; /* Number of blocks in filesystem */ +static int sector_size = 512; /* Size of a logical sector */ +static int sector_size_set = 0; /* User selected sector size */ +static int backup_boot = 0; /* Sector# of backup boot sector */ +static int reserved_sectors = 0; /* Number of reserved sectors */ +static int badblocks = 0; /* Number of bad blocks in the filesystem */ +static int nr_fats = 2; /* Default number of FATs to produce */ +static int size_fat = 0; /* Size in bits of FAT entries */ +static int size_fat_by_user = 0; /* 1 if FAT size user selected */ +static int dev = -1; /* FS block device file handle */ +static int ignore_full_disk = 0; /* Ignore warning about 'full' disk devices */ +static off_t currently_testing = 0; /* Block currently being tested (if autodetect bad blocks) */ +static struct msdos_boot_sector bs; /* Boot sector data */ +static int start_data_sector; /* Sector number for the start of the data area */ +static int start_data_block; /* Block number for the start of the data area */ +static unsigned char *fat; /* File allocation table */ +static unsigned alloced_fat_length; /* # of FAT sectors we can keep in memory */ +static unsigned char *info_sector; /* FAT32 info sector */ +static struct msdos_dir_entry *root_dir; /* Root directory */ +static int size_root_dir; /* Size of the root directory in bytes */ +static int sectors_per_cluster = 0; /* Number of sectors per disk cluster */ +static int root_dir_entries = 0; /* Number of root directory entries */ +static char *blank_sector; /* Blank sector - all zeros */ +static int hidden_sectors = 0; /* Number of hidden sectors */ +static int malloc_entire_fat = FALSE; /* Whether we should malloc() the entire FAT or not */ +static int align_structures = TRUE; /* Whether to enforce alignment */ +static int orphaned_sectors = 0; /* Sectors that exist in the last block of filesystem */ + +/* Function prototype definitions */ + +static void fatal_error(const char *fmt_string) __attribute__ ((noreturn)); +static void mark_FAT_cluster(int cluster, unsigned int value); +static void mark_FAT_sector(int sector, unsigned int value); +static long do_check(char *buffer, int try, off_t current_block); +static void alarm_intr(int alnum); +static void check_blocks(void); +static void get_list_blocks(char *filename); +static int valid_offset(int fd, loff_t offset); +static unsigned long long count_blocks(char *filename, int *remainder); +static void check_mount(char *device_name); +static void establish_params(int device_num, int size); +static void setup_tables(void); +static void write_tables(void); + +/* The function implementations */ + +/* Handle the reporting of fatal errors. Volatile to let gcc know that this doesn't return */ + +static void fatal_error(const char *fmt_string) +{ + fprintf(stderr, fmt_string, program_name, device_name); + exit(1); /* The error exit code is 1! */ +} + +/* Mark the specified cluster as having a particular value */ + +static void mark_FAT_cluster(int cluster, unsigned int value) +{ + switch (size_fat) { + case 12: + value &= 0x0fff; + if (((cluster * 3) & 0x1) == 0) { + fat[3 * cluster / 2] = (unsigned char)(value & 0x00ff); + fat[(3 * cluster / 2) + 1] = + (unsigned char)((fat[(3 * cluster / 2) + 1] & 0x00f0) + | ((value & 0x0f00) >> 8)); + } else { + fat[3 * cluster / 2] = + (unsigned char)((fat[3 * cluster / 2] & 0x000f) | + ((value & 0x000f) << 4)); + fat[(3 * cluster / 2) + 1] = (unsigned char)((value & 0x0ff0) >> 4); + } + break; + + case 16: + value &= 0xffff; + fat[2 * cluster] = (unsigned char)(value & 0x00ff); + fat[(2 * cluster) + 1] = (unsigned char)(value >> 8); + break; + + case 32: + value &= 0xfffffff; + fat[4 * cluster] = (unsigned char)(value & 0x000000ff); + fat[(4 * cluster) + 1] = (unsigned char)((value & 0x0000ff00) >> 8); + fat[(4 * cluster) + 2] = (unsigned char)((value & 0x00ff0000) >> 16); + fat[(4 * cluster) + 3] = (unsigned char)((value & 0xff000000) >> 24); + break; + + default: + die("Bad FAT size (not 12, 16, or 32)"); + } +} + +/* Mark a specified sector as having a particular value in it's FAT entry */ + +static void mark_FAT_sector(int sector, unsigned int value) +{ + int cluster; + + cluster = (sector - start_data_sector) / (int)(bs.cluster_size) / + (sector_size / HARD_SECTOR_SIZE); + if (cluster < 0) + die("Invalid cluster number in mark_FAT_sector: probably bug!"); + + mark_FAT_cluster(cluster, value); +} + +/* Perform a test on a block. Return the number of blocks that could be read successfully */ + +static long do_check(char *buffer, int try, off_t current_block) +{ + long got; + + if (llseek(dev, current_block * BLOCK_SIZE, SEEK_SET) /* Seek to the correct location */ + !=current_block * BLOCK_SIZE) + die("seek failed during testing for blocks"); + + got = read(dev, buffer, try * BLOCK_SIZE); /* Try reading! */ + if (got < 0) + got = 0; + + if (got & (BLOCK_SIZE - 1)) + printf("Unexpected values in do_check: probably bugs\n"); + got /= BLOCK_SIZE; + + return got; +} + +/* Alarm clock handler - display the status of the quest for bad blocks! Then retrigger the alarm for five senconds + later (so we can come here again) */ + +static void alarm_intr(int alnum) +{ + if (currently_testing >= blocks) + return; + + signal(SIGALRM, alarm_intr); + alarm(5); + if (!currently_testing) + return; + + printf("%lld... ", (unsigned long long)currently_testing); + fflush(stdout); +} + +static void check_blocks(void) +{ + int try, got; + int i; + static char blkbuf[BLOCK_SIZE * TEST_BUFFER_BLOCKS]; + + if (verbose) { + printf("Searching for bad blocks "); + fflush(stdout); + } + currently_testing = 0; + if (verbose) { + signal(SIGALRM, alarm_intr); + alarm(5); + } + try = TEST_BUFFER_BLOCKS; + while (currently_testing < blocks) { + if (currently_testing + try > blocks) + try = blocks - currently_testing; + got = do_check(blkbuf, try, currently_testing); + currently_testing += got; + if (got == try) { + try = TEST_BUFFER_BLOCKS; + continue; + } else + try = 1; + if (currently_testing < start_data_block) + die("bad blocks before data-area: cannot make fs"); + + for (i = 0; i < SECTORS_PER_BLOCK; i++) /* Mark all of the sectors in the block as bad */ + mark_sector_bad(currently_testing * SECTORS_PER_BLOCK + i); + badblocks++; + currently_testing++; + } + + if (verbose) + printf("\n"); + + if (badblocks) + printf("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : ""); +} + +static void get_list_blocks(char *filename) +{ + int i; + FILE *listfile; + unsigned long blockno; + + listfile = fopen(filename, "r"); + if (listfile == (FILE *) NULL) + die("Can't open file of bad blocks"); + + while (!feof(listfile)) { + fscanf(listfile, "%ld\n", &blockno); + for (i = 0; i < SECTORS_PER_BLOCK; i++) /* Mark all of the sectors in the block as bad */ + mark_sector_bad(blockno * SECTORS_PER_BLOCK + i); + badblocks++; + } + fclose(listfile); + + if (badblocks) + printf("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : ""); +} + +/* Given a file descriptor and an offset, check whether the offset is a valid offset for the file - return FALSE if it + isn't valid or TRUE if it is */ + +static int valid_offset(int fd, loff_t offset) +{ + char ch; + + if (llseek(fd, offset, SEEK_SET) < 0) + return FALSE; + if (read(fd, &ch, 1) < 1) + return FALSE; + return TRUE; +} + +/* Given a filename, look to see how many blocks of BLOCK_SIZE are present, returning the answer */ + +static unsigned long long count_blocks(char *filename, int *remainder) + +{ + loff_t high, low; + int fd; + + if ((fd = open(filename, O_RDONLY)) < 0) { + perror(filename); + exit(1); + } + + /* first try SEEK_END, which should work on most devices nowadays */ + if ((low = llseek(fd, 0, SEEK_END)) <= 0) { + low = 0; + for (high = 1; valid_offset(fd, high); high *= 2) + low = high; + while (low < high - 1) { + const loff_t mid = (low + high) / 2; + if (valid_offset(fd, mid)) + low = mid; + else + high = mid; + } + ++low; + } + + close(fd); + *remainder = (low%BLOCK_SIZE)/sector_size; + return(low / BLOCK_SIZE); +} + +/* Check to see if the specified device is currently mounted - abort if it is */ + +static void check_mount(char *device_name) +{ + FILE *f; + struct mntent *mnt; + + if ((f = setmntent(MOUNTED, "r")) == NULL) + return; + while ((mnt = getmntent(f)) != NULL) + if (strcmp(device_name, mnt->mnt_fsname) == 0) + die("%s contains a mounted file system."); + endmntent(f); +} + +/* Establish the geometry and media parameters for the device */ + +static void establish_params(int device_num, int size) +{ + long loop_size; + struct hd_geometry geometry; + struct floppy_struct param; + int def_root_dir_entries = 512; + + if ((0 == device_num) || ((device_num & 0xff00) == 0x0200)) + /* file image or floppy disk */ + { + if (0 == device_num) { + param.size = size / 512; + switch (param.size) { + case 720: + param.sect = 9; + param.head = 2; + break; + case 1440: + param.sect = 9; + param.head = 2; + break; + case 2400: + param.sect = 15; + param.head = 2; + break; + case 2880: + param.sect = 18; + param.head = 2; + break; + case 5760: + param.sect = 36; + param.head = 2; + break; + default: + /* fake values */ + param.sect = 32; + param.head = 64; + break; + } + + } else { /* is a floppy diskette */ + + if (ioctl(dev, FDGETPRM, ¶m)) /* Can we get the diskette geometry? */ + die("unable to get diskette geometry for '%s'"); + } + bs.secs_track = CT_LE_W(param.sect); /* Set up the geometry information */ + bs.heads = CT_LE_W(param.head); + switch (param.size) { /* Set up the media descriptor byte */ + case 720: /* 5.25", 2, 9, 40 - 360K */ + bs.media = (char)0xfd; + bs.cluster_size = (char)2; + def_root_dir_entries = 112; + break; + + case 1440: /* 3.5", 2, 9, 80 - 720K */ + bs.media = (char)0xf9; + bs.cluster_size = (char)2; + def_root_dir_entries = 112; + break; + + case 2400: /* 5.25", 2, 15, 80 - 1200K */ + bs.media = (char)0xf9; + bs.cluster_size = (char)(atari_format ? 2 : 1); + def_root_dir_entries = 224; + break; + + case 5760: /* 3.5", 2, 36, 80 - 2880K */ + bs.media = (char)0xf0; + bs.cluster_size = (char)2; + def_root_dir_entries = 224; + break; + + case 2880: /* 3.5", 2, 18, 80 - 1440K */ +floppy_default: + bs.media = (char)0xf0; + bs.cluster_size = (char)(atari_format ? 2 : 1); + def_root_dir_entries = 224; + break; + + default: /* Anything else */ + if (0 == device_num) + goto def_hd_params; + else + goto floppy_default; + } + } else if ((device_num & 0xff00) == 0x0700) { /* This is a loop device */ + if (ioctl(dev, BLKGETSIZE, &loop_size)) + die("unable to get loop device size"); + + switch (loop_size) { /* Assuming the loop device -> floppy later */ + case 720: /* 5.25", 2, 9, 40 - 360K */ + bs.secs_track = CF_LE_W(9); + bs.heads = CF_LE_W(2); + bs.media = (char)0xfd; + bs.cluster_size = (char)2; + def_root_dir_entries = 112; + break; + + case 1440: /* 3.5", 2, 9, 80 - 720K */ + bs.secs_track = CF_LE_W(9); + bs.heads = CF_LE_W(2); + bs.media = (char)0xf9; + bs.cluster_size = (char)2; + def_root_dir_entries = 112; + break; + + case 2400: /* 5.25", 2, 15, 80 - 1200K */ + bs.secs_track = CF_LE_W(15); + bs.heads = CF_LE_W(2); + bs.media = (char)0xf9; + bs.cluster_size = (char)(atari_format ? 2 : 1); + def_root_dir_entries = 224; + break; + + case 5760: /* 3.5", 2, 36, 80 - 2880K */ + bs.secs_track = CF_LE_W(36); + bs.heads = CF_LE_W(2); + bs.media = (char)0xf0; + bs.cluster_size = (char)2; + bs.dir_entries[0] = (char)224; + bs.dir_entries[1] = (char)0; + break; + + case 2880: /* 3.5", 2, 18, 80 - 1440K */ + bs.secs_track = CF_LE_W(18); + bs.heads = CF_LE_W(2); + bs.media = (char)0xf0; + bs.cluster_size = (char)(atari_format ? 2 : 1); + def_root_dir_entries = 224; + break; + + default: /* Anything else: default hd setup */ + printf("Loop device does not match a floppy size, using " + "default hd params\n"); + bs.secs_track = CT_LE_W(32); /* these are fake values... */ + bs.heads = CT_LE_W(64); + goto def_hd_params; + } + } else + /* Must be a hard disk then! */ + { + /* Can we get the drive geometry? (Note I'm not too sure about */ + /* whether to use HDIO_GETGEO or HDIO_REQ) */ + if (ioctl(dev, HDIO_GETGEO, &geometry) || geometry.sectors == 0 + || geometry.heads == 0) { + printf("unable to get drive geometry, using default 255/63\n"); + bs.secs_track = CT_LE_W(63); + bs.heads = CT_LE_W(255); + } else { + bs.secs_track = CT_LE_W(geometry.sectors); /* Set up the geometry information */ + bs.heads = CT_LE_W(geometry.heads); + } +def_hd_params: + bs.media = (char)0xf8; /* Set up the media descriptor for a hard drive */ + if (!size_fat && blocks * SECTORS_PER_BLOCK > 1064960) { + if (verbose) + printf("Auto-selecting FAT32 for large filesystem\n"); + size_fat = 32; + } + if (size_fat == 32) { + /* For FAT32, try to do the same as M$'s format command + * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20): + * fs size <= 260M: 0.5k clusters + * fs size <= 8G: 4k clusters + * fs size <= 16G: 8k clusters + * fs size > 16G: 16k clusters + */ + unsigned long sz_mb = + (blocks + (1 << (20 - BLOCK_SIZE_BITS)) - 1) >> (20 - + BLOCK_SIZE_BITS); + bs.cluster_size = + sz_mb > 16 * 1024 ? 32 : sz_mb > 8 * 1024 ? 16 : sz_mb > + 260 ? 8 : 1; + } else { + /* FAT12 and FAT16: start at 4 sectors per cluster */ + bs.cluster_size = (char)4; + } + } + + if (!root_dir_entries) + root_dir_entries = def_root_dir_entries; +} + +/* + * If alignment is enabled, round the first argument up to the second; the + * latter must be a power of two. + */ +static unsigned int align_object(unsigned int sectors, unsigned int clustsize) +{ + if (align_structures) + return (sectors + clustsize - 1) & ~(clustsize - 1); + else + return sectors; +} + +/* Create the filesystem data tables */ + +static void setup_tables(void) +{ + unsigned num_sectors; + unsigned cluster_count = 0, fat_length; + struct tm *ctime; + struct msdos_volume_info *vi = + (size_fat == 32 ? &bs.fat32.vi : &bs.oldfat.vi); + + if (atari_format) + /* On Atari, the first few bytes of the boot sector are assigned + * differently: The jump code is only 2 bytes (and m68k machine code + * :-), then 6 bytes filler (ignored), then 3 byte serial number. */ + memcpy(bs.system_id - 1, "mkdosf", 6); + else + strcpy((char *)bs.system_id, "mkdosfs"); + if (sectors_per_cluster) + bs.cluster_size = (char)sectors_per_cluster; + if (size_fat == 32) { + /* Under FAT32, the root dir is in a cluster chain, and this is + * signalled by bs.dir_entries being 0. */ + root_dir_entries = 0; + } + + if (atari_format) { + bs.system_id[5] = (unsigned char)(volume_id & 0x000000ff); + bs.system_id[6] = (unsigned char)((volume_id & 0x0000ff00) >> 8); + bs.system_id[7] = (unsigned char)((volume_id & 0x00ff0000) >> 16); + } else { + vi->volume_id[0] = (unsigned char)(volume_id & 0x000000ff); + vi->volume_id[1] = (unsigned char)((volume_id & 0x0000ff00) >> 8); + vi->volume_id[2] = (unsigned char)((volume_id & 0x00ff0000) >> 16); + vi->volume_id[3] = (unsigned char)(volume_id >> 24); + } + + if (!atari_format) { + memcpy(vi->volume_label, volume_name, 11); + + memcpy(bs.boot_jump, dummy_boot_jump, 3); + /* Patch in the correct offset to the boot code */ + bs.boot_jump[1] = ((size_fat == 32 ? + (char *)&bs.fat32.boot_code : + (char *)&bs.oldfat.boot_code) - (char *)&bs) - 2; + + if (size_fat == 32) { + int offset = (char *)&bs.fat32.boot_code - + (char *)&bs + MESSAGE_OFFSET + 0x7c00; + if (dummy_boot_code[BOOTCODE_FAT32_SIZE - 1]) + printf("Warning: message too long; truncated\n"); + dummy_boot_code[BOOTCODE_FAT32_SIZE - 1] = 0; + memcpy(bs.fat32.boot_code, dummy_boot_code, BOOTCODE_FAT32_SIZE); + bs.fat32.boot_code[MSG_OFFSET_OFFSET] = offset & 0xff; + bs.fat32.boot_code[MSG_OFFSET_OFFSET + 1] = offset >> 8; + } else { + memcpy(bs.oldfat.boot_code, dummy_boot_code, BOOTCODE_SIZE); + } + bs.boot_sign = CT_LE_W(BOOT_SIGN); + } else { + memcpy(bs.boot_jump, dummy_boot_jump_m68k, 2); + } + if (verbose >= 2) + printf("Boot jump code is %02x %02x\n", + bs.boot_jump[0], bs.boot_jump[1]); + + if (!reserved_sectors) + reserved_sectors = (size_fat == 32) ? 32 : 1; + else { + if (size_fat == 32 && reserved_sectors < 2) + die("On FAT32 at least 2 reserved sectors are needed."); + } + bs.reserved = CT_LE_W(reserved_sectors); + if (verbose >= 2) + printf("Using %d reserved sectors\n", reserved_sectors); + bs.fats = (char)nr_fats; + if (!atari_format || size_fat == 32) + bs.hidden = CT_LE_L(hidden_sectors); + else { + /* In Atari format, hidden is a 16 bit field */ + __u16 hidden = CT_LE_W(hidden_sectors); + if (hidden_sectors & ~0xffff) + die("#hidden doesn't fit in 16bit field of Atari format\n"); + memcpy(&bs.hidden, &hidden, 2); + } + + num_sectors = (long long)(blocks *BLOCK_SIZE / sector_size)+orphaned_sectors; + + if (!atari_format) { + unsigned fatdata1216; /* Sectors for FATs + data area (FAT12/16) */ + unsigned fatdata32; /* Sectors for FATs + data area (FAT32) */ + unsigned fatlength12, fatlength16, fatlength32; + unsigned maxclust12, maxclust16, maxclust32; + unsigned clust12, clust16, clust32; + int maxclustsize; + unsigned root_dir_sectors = cdiv(root_dir_entries * 32, sector_size); + + /* + * If the filesystem is 8192 sectors or less (4 MB with 512-byte + * sectors, i.e. floppy size), don't align the data structures. + */ + if (num_sectors <= 8192) { + if (align_structures && verbose >= 2) + printf("Disabling alignment due to tiny filesystem\n"); + + align_structures = FALSE; + } + + if (sectors_per_cluster) + bs.cluster_size = maxclustsize = sectors_per_cluster; + else + /* An initial guess for bs.cluster_size should already be set */ + maxclustsize = 128; + + do { + fatdata32 = num_sectors + - align_object(reserved_sectors, bs.cluster_size); + fatdata1216 = fatdata32 + - align_object(root_dir_sectors, bs.cluster_size); + + if (verbose >= 2) + printf("Trying with %d sectors/cluster:\n", bs.cluster_size); + + /* The factor 2 below avoids cut-off errors for nr_fats == 1. + * The "nr_fats*3" is for the reserved first two FAT entries */ + clust12 = 2 * ((long long)fatdata1216 * sector_size + nr_fats * 3) / + (2 * (int)bs.cluster_size * sector_size + nr_fats * 3); + fatlength12 = cdiv(((clust12 + 2) * 3 + 1) >> 1, sector_size); + fatlength12 = align_object(fatlength12, bs.cluster_size); + /* Need to recalculate number of clusters, since the unused parts of the + * FATS and data area together could make up space for an additional, + * not really present cluster. */ + clust12 = (fatdata1216 - nr_fats * fatlength12) / bs.cluster_size; + maxclust12 = (fatlength12 * 2 * sector_size) / 3; + if (maxclust12 > MAX_CLUST_12) + maxclust12 = MAX_CLUST_12; + if (verbose >= 2) + printf("FAT12: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", + clust12, fatlength12, maxclust12, MAX_CLUST_12); + if (clust12 > maxclust12 - 2) { + clust12 = 0; + if (verbose >= 2) + printf("FAT12: too much clusters\n"); + } + + clust16 = ((long long)fatdata1216 * sector_size + nr_fats * 4) / + ((int)bs.cluster_size * sector_size + nr_fats * 2); + fatlength16 = cdiv((clust16 + 2) * 2, sector_size); + fatlength16 = align_object(fatlength16, bs.cluster_size); + /* Need to recalculate number of clusters, since the unused parts of the + * FATS and data area together could make up space for an additional, + * not really present cluster. */ + clust16 = (fatdata1216 - nr_fats * fatlength16) / bs.cluster_size; + maxclust16 = (fatlength16 * sector_size) / 2; + if (maxclust16 > MAX_CLUST_16) + maxclust16 = MAX_CLUST_16; + if (verbose >= 2) + printf("FAT16: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", + clust16, fatlength16, maxclust16, MAX_CLUST_16); + if (clust16 > maxclust16 - 2) { + if (verbose >= 2) + printf("FAT16: too much clusters\n"); + clust16 = 0; + } + /* The < 4078 avoids that the filesystem will be misdetected as having a + * 12 bit FAT. */ + if (clust16 < FAT12_THRESHOLD + && !(size_fat_by_user && size_fat == 16)) { + if (verbose >= 2) + printf(clust16 < FAT12_THRESHOLD ? + "FAT16: would be misdetected as FAT12\n" : + "FAT16: too much clusters\n"); + clust16 = 0; + } + + clust32 = ((long long)fatdata32 * sector_size + nr_fats * 8) / + ((int)bs.cluster_size * sector_size + nr_fats * 4); + fatlength32 = cdiv((clust32 + 2) * 4, sector_size); + fatlength32 = align_object(fatlength32, bs.cluster_size); + /* Need to recalculate number of clusters, since the unused parts of the + * FATS and data area together could make up space for an additional, + * not really present cluster. */ + clust32 = (fatdata32 - nr_fats * fatlength32) / bs.cluster_size; + maxclust32 = (fatlength32 * sector_size) / 4; + if (maxclust32 > MAX_CLUST_32) + maxclust32 = MAX_CLUST_32; + if (clust32 && clust32 < MIN_CLUST_32 + && !(size_fat_by_user && size_fat == 32)) { + clust32 = 0; + if (verbose >= 2) + printf("FAT32: not enough clusters (%d)\n", MIN_CLUST_32); + } + if (verbose >= 2) + printf("FAT32: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", + clust32, fatlength32, maxclust32, MAX_CLUST_32); + if (clust32 > maxclust32) { + clust32 = 0; + if (verbose >= 2) + printf("FAT32: too much clusters\n"); + } + + if ((clust12 && (size_fat == 0 || size_fat == 12)) || + (clust16 && (size_fat == 0 || size_fat == 16)) || + (clust32 && size_fat == 32)) + break; + + bs.cluster_size <<= 1; + } while (bs.cluster_size && bs.cluster_size <= maxclustsize); + + /* Use the optimal FAT size if not specified; + * FAT32 is (not yet) choosen automatically */ + if (!size_fat) { + size_fat = (clust16 > clust12) ? 16 : 12; + if (verbose >= 2) + printf("Choosing %d bits for FAT\n", size_fat); + } + + switch (size_fat) { + case 12: + cluster_count = clust12; + fat_length = fatlength12; + bs.fat_length = CT_LE_W(fatlength12); + memcpy(vi->fs_type, MSDOS_FAT12_SIGN, 8); + break; + + case 16: + if (clust16 < FAT12_THRESHOLD) { + if (size_fat_by_user) { + fprintf(stderr, "WARNING: Not enough clusters for a " + "16 bit FAT! The filesystem will be\n" + "misinterpreted as having a 12 bit FAT without " + "mount option \"fat=16\".\n"); + } else { + fprintf(stderr, "This filesystem has an unfortunate size. " + "A 12 bit FAT cannot provide\n" + "enough clusters, but a 16 bit FAT takes up a little " + "bit more space so that\n" + "the total number of clusters becomes less than the " + "threshold value for\n" + "distinction between 12 and 16 bit FATs.\n"); + die("Make the file system a bit smaller manually."); + } + } + cluster_count = clust16; + fat_length = fatlength16; + bs.fat_length = CT_LE_W(fatlength16); + memcpy(vi->fs_type, MSDOS_FAT16_SIGN, 8); + break; + + case 32: + if (clust32 < MIN_CLUST_32) + fprintf(stderr, + "WARNING: Not enough clusters for a 32 bit FAT!\n"); + cluster_count = clust32; + fat_length = fatlength32; + bs.fat_length = CT_LE_W(0); + bs.fat32.fat32_length = CT_LE_L(fatlength32); + memcpy(vi->fs_type, MSDOS_FAT32_SIGN, 8); + root_dir_entries = 0; + break; + + default: + die("FAT not 12, 16 or 32 bits"); + } + + /* Adjust the reserved number of sectors for alignment */ + reserved_sectors = align_object(reserved_sectors, bs.cluster_size); + bs.reserved = CT_LE_W(reserved_sectors); + + /* Adjust the number of root directory entries to help enforce alignment */ + if (align_structures) { + root_dir_entries = align_object(root_dir_sectors, bs.cluster_size) + * (sector_size >> 5); + } + } else { + unsigned clusters, maxclust, fatdata; + + /* GEMDOS always uses a 12 bit FAT on floppies, and always a 16 bit FAT on + * hard disks. So use 12 bit if the size of the file system suggests that + * this fs is for a floppy disk, if the user hasn't explicitly requested a + * size. + */ + if (!size_fat) + size_fat = (num_sectors == 1440 || num_sectors == 2400 || + num_sectors == 2880 || num_sectors == 5760) ? 12 : 16; + if (verbose >= 2) + printf("Choosing %d bits for FAT\n", size_fat); + + /* Atari format: cluster size should be 2, except explicitly requested by + * the user, since GEMDOS doesn't like other cluster sizes very much. + * Instead, tune the sector size for the FS to fit. + */ + bs.cluster_size = sectors_per_cluster ? sectors_per_cluster : 2; + if (!sector_size_set) { + while (num_sectors > GEMDOS_MAX_SECTORS) { + num_sectors >>= 1; + sector_size <<= 1; + } + } + if (verbose >= 2) + printf("Sector size must be %d to have less than %d log. sectors\n", + sector_size, GEMDOS_MAX_SECTORS); + + /* Check if there are enough FAT indices for how much clusters we have */ + do { + fatdata = num_sectors - cdiv(root_dir_entries * 32, sector_size) - + reserved_sectors; + /* The factor 2 below avoids cut-off errors for nr_fats == 1 and + * size_fat == 12 + * The "2*nr_fats*size_fat/8" is for the reserved first two FAT entries + */ + clusters = + (2 * + ((long long)fatdata * sector_size - + 2 * nr_fats * size_fat / 8)) / (2 * ((int)bs.cluster_size * + sector_size + + nr_fats * size_fat / 8)); + fat_length = cdiv((clusters + 2) * size_fat / 8, sector_size); + /* Need to recalculate number of clusters, since the unused parts of the + * FATS and data area together could make up space for an additional, + * not really present cluster. */ + clusters = (fatdata - nr_fats * fat_length) / bs.cluster_size; + maxclust = (fat_length * sector_size * 8) / size_fat; + if (verbose >= 2) + printf("ss=%d: #clu=%d, fat_len=%d, maxclu=%d\n", + sector_size, clusters, fat_length, maxclust); + + /* last 10 cluster numbers are special (except FAT32: 4 high bits rsvd); + * first two numbers are reserved */ + if (maxclust <= + (size_fat == 32 ? MAX_CLUST_32 : (1 << size_fat) - 0x10) + && clusters <= maxclust - 2) + break; + if (verbose >= 2) + printf(clusters > maxclust - 2 ? + "Too many clusters\n" : "FAT too big\n"); + + /* need to increment sector_size once more to */ + if (sector_size_set) + die("With this sector size, the maximum number of FAT entries " + "would be exceeded."); + num_sectors >>= 1; + sector_size <<= 1; + } while (sector_size <= GEMDOS_MAX_SECTOR_SIZE); + + if (sector_size > GEMDOS_MAX_SECTOR_SIZE) + die("Would need a sector size > 16k, which GEMDOS can't work with"); + + cluster_count = clusters; + if (size_fat != 32) + bs.fat_length = CT_LE_W(fat_length); + else { + bs.fat_length = 0; + bs.fat32.fat32_length = CT_LE_L(fat_length); + } + } + + bs.sector_size[0] = (char)(sector_size & 0x00ff); + bs.sector_size[1] = (char)((sector_size & 0xff00) >> 8); + + bs.dir_entries[0] = (char)(root_dir_entries & 0x00ff); + bs.dir_entries[1] = (char)((root_dir_entries & 0xff00) >> 8); + + if (size_fat == 32) { + /* set up additional FAT32 fields */ + bs.fat32.flags = CT_LE_W(0); + bs.fat32.version[0] = 0; + bs.fat32.version[1] = 0; + bs.fat32.root_cluster = CT_LE_L(2); + bs.fat32.info_sector = CT_LE_W(1); + if (!backup_boot) + backup_boot = (reserved_sectors >= 7) ? 6 : + (reserved_sectors >= 2) ? reserved_sectors - 1 : 0; + else { + if (backup_boot == 1) + die("Backup boot sector must be after sector 1"); + else if (backup_boot >= reserved_sectors) + die("Backup boot sector must be a reserved sector"); + } + if (verbose >= 2) + printf("Using sector %d as backup boot sector (0 = none)\n", + backup_boot); + bs.fat32.backup_boot = CT_LE_W(backup_boot); + memset(&bs.fat32.reserved2, 0, sizeof(bs.fat32.reserved2)); + } + + if (atari_format) { + /* Just some consistency checks */ + if (num_sectors >= GEMDOS_MAX_SECTORS) + die("GEMDOS can't handle more than 65531 sectors"); + else if (num_sectors >= OLDGEMDOS_MAX_SECTORS) + printf("Warning: More than 32765 sector need TOS 1.04 " + "or higher.\n"); + } + if (num_sectors >= 65536) { + bs.sectors[0] = (char)0; + bs.sectors[1] = (char)0; + bs.total_sect = CT_LE_L(num_sectors); + } else { + bs.sectors[0] = (char)(num_sectors & 0x00ff); + bs.sectors[1] = (char)((num_sectors & 0xff00) >> 8); + if (!atari_format) + bs.total_sect = CT_LE_L(0); + } + + if (!atari_format) + vi->ext_boot_sign = MSDOS_EXT_SIGN; + + if (!cluster_count) { + if (sectors_per_cluster) /* If yes, die if we'd spec'd sectors per cluster */ + die("Too many clusters for file system - try more sectors per cluster"); + else + die("Attempting to create a too large file system"); + } + + /* The two following vars are in hard sectors, i.e. 512 byte sectors! */ + start_data_sector = (reserved_sectors + nr_fats * fat_length) * + (sector_size / HARD_SECTOR_SIZE); + start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / + SECTORS_PER_BLOCK; + + if (blocks < start_data_block + 32) /* Arbitrary undersize file system! */ + die("Too few blocks for viable file system"); + + if (verbose) { + printf("%s has %d head%s and %d sector%s per track,\n", + device_name, CF_LE_W(bs.heads), + (CF_LE_W(bs.heads) != 1) ? "s" : "", CF_LE_W(bs.secs_track), + (CF_LE_W(bs.secs_track) != 1) ? "s" : ""); + printf("logical sector size is %d,\n", sector_size); + printf("using 0x%02x media descriptor, with %d sectors;\n", + (int)(bs.media), num_sectors); + printf("file system has %d %d-bit FAT%s and %d sector%s per cluster.\n", + (int)(bs.fats), size_fat, (bs.fats != 1) ? "s" : "", + (int)(bs.cluster_size), (bs.cluster_size != 1) ? "s" : ""); + printf("FAT size is %d sector%s, and provides %d cluster%s.\n", + fat_length, (fat_length != 1) ? "s" : "", + cluster_count, (cluster_count != 1) ? "s" : ""); + printf("There %s %u reserved sector%s.\n", + (reserved_sectors != 1) ? "are" : "is", + reserved_sectors, (reserved_sectors != 1) ? "s" : ""); + + if (size_fat != 32) { + unsigned root_dir_entries = + bs.dir_entries[0] + ((bs.dir_entries[1]) * 256); + unsigned root_dir_sectors = + cdiv(root_dir_entries * 32, sector_size); + printf("Root directory contains %u slots and uses %u sectors.\n", + root_dir_entries, root_dir_sectors); + } + printf("Volume ID is %08lx, ", volume_id & + (atari_format ? 0x00ffffff : 0xffffffff)); + if (strcmp(volume_name, NO_NAME)) + printf("volume label %s.\n", volume_name); + else + printf("no volume label.\n"); + } + + /* Make the file allocation tables! */ + + if (malloc_entire_fat) + alloced_fat_length = fat_length; + else + alloced_fat_length = 1; + + if ((fat = + (unsigned char *)malloc(alloced_fat_length * sector_size)) == NULL) + die("unable to allocate space for FAT image in memory"); + + memset(fat, 0, alloced_fat_length * sector_size); + + mark_FAT_cluster(0, 0xffffffff); /* Initial fat entries */ + mark_FAT_cluster(1, 0xffffffff); + fat[0] = (unsigned char)bs.media; /* Put media type in first byte! */ + if (size_fat == 32) { + /* Mark cluster 2 as EOF (used for root dir) */ + mark_FAT_cluster(2, FAT_EOF); + } + + /* Make the root directory entries */ + + size_root_dir = (size_fat == 32) ? + bs.cluster_size * sector_size : + (((int)bs.dir_entries[1] * 256 + (int)bs.dir_entries[0]) * + sizeof(struct msdos_dir_entry)); + if ((root_dir = (struct msdos_dir_entry *)malloc(size_root_dir)) == NULL) { + free(fat); /* Tidy up before we die! */ + die("unable to allocate space for root directory in memory"); + } + + memset(root_dir, 0, size_root_dir); + if (memcmp(volume_name, NO_NAME, 11)) { + struct msdos_dir_entry *de = &root_dir[0]; + memcpy(de->name, volume_name, 8); + memcpy(de->ext, volume_name + 8, 3); + de->attr = ATTR_VOLUME; + ctime = localtime(&create_time); + de->time = CT_LE_W((unsigned short)((ctime->tm_sec >> 1) + + (ctime->tm_min << 5) + + (ctime->tm_hour << 11))); + de->date = + CT_LE_W((unsigned short)(ctime->tm_mday + + ((ctime->tm_mon + 1) << 5) + + ((ctime->tm_year - 80) << 9))); + de->ctime_ms = 0; + de->ctime = de->time; + de->cdate = de->date; + de->adate = de->date; + de->starthi = CT_LE_W(0); + de->start = CT_LE_W(0); + de->size = CT_LE_L(0); + } + + if (size_fat == 32) { + /* For FAT32, create an info sector */ + struct fat32_fsinfo *info; + + if (!(info_sector = malloc(sector_size))) + die("Out of memory"); + memset(info_sector, 0, sector_size); + /* fsinfo structure is at offset 0x1e0 in info sector by observation */ + info = (struct fat32_fsinfo *)(info_sector + 0x1e0); + + /* Info sector magic */ + info_sector[0] = 'R'; + info_sector[1] = 'R'; + info_sector[2] = 'a'; + info_sector[3] = 'A'; + + /* Magic for fsinfo structure */ + info->signature = CT_LE_L(0x61417272); + /* We've allocated cluster 2 for the root dir. */ + info->free_clusters = CT_LE_L(cluster_count - 1); + info->next_cluster = CT_LE_L(2); + + /* Info sector also must have boot sign */ + *(__u16 *) (info_sector + 0x1fe) = CT_LE_W(BOOT_SIGN); + } + + if (!(blank_sector = malloc(sector_size))) + die("Out of memory"); + memset(blank_sector, 0, sector_size); +} + +/* Write the new filesystem's data tables to wherever they're going to end up! */ + +#define error(str) \ + do { \ + free (fat); \ + if (info_sector) free (info_sector); \ + free (root_dir); \ + die (str); \ + } while(0) + +#define seekto(pos,errstr) \ + do { \ + loff_t __pos = (pos); \ + if (llseek (dev, __pos, SEEK_SET) != __pos) \ + error ("seek to " errstr " failed whilst writing tables"); \ + } while(0) + +#define writebuf(buf,size,errstr) \ + do { \ + int __size = (size); \ + if (write (dev, buf, __size) != __size) \ + error ("failed whilst writing " errstr); \ + } while(0) + +static void write_tables(void) +{ + int x; + int fat_length; + + fat_length = (size_fat == 32) ? + CF_LE_L(bs.fat32.fat32_length) : CF_LE_W(bs.fat_length); + + seekto(0, "start of device"); + /* clear all reserved sectors */ + for (x = 0; x < reserved_sectors; ++x) + writebuf(blank_sector, sector_size, "reserved sector"); + /* seek back to sector 0 and write the boot sector */ + seekto(0, "boot sector"); + writebuf((char *)&bs, sizeof(struct msdos_boot_sector), "boot sector"); + /* on FAT32, write the info sector and backup boot sector */ + if (size_fat == 32) { + seekto(CF_LE_W(bs.fat32.info_sector) * sector_size, "info sector"); + writebuf(info_sector, 512, "info sector"); + if (backup_boot != 0) { + seekto(backup_boot * sector_size, "backup boot sector"); + writebuf((char *)&bs, sizeof(struct msdos_boot_sector), + "backup boot sector"); + } + } + /* seek to start of FATS and write them all */ + seekto(reserved_sectors * sector_size, "first FAT"); + for (x = 1; x <= nr_fats; x++) { + int y; + int blank_fat_length = fat_length - alloced_fat_length; + writebuf(fat, alloced_fat_length * sector_size, "FAT"); + for (y = 0; y < blank_fat_length; y++) + writebuf(blank_sector, sector_size, "FAT"); + } + /* Write the root directory directly after the last FAT. This is the root + * dir area on FAT12/16, and the first cluster on FAT32. */ + writebuf((char *)root_dir, size_root_dir, "root directory"); + + if (blank_sector) + free(blank_sector); + if (info_sector) + free(info_sector); + free(root_dir); /* Free up the root directory space from setup_tables */ + free(fat); /* Free up the fat table space reserved during setup_tables */ +} + +/* Report the command usage and return a failure error code */ + +void usage(void) +{ + fatal_error("\ +Usage: mkdosfs [-a][-A][-c][-C][-v][-I][-l bad-block-file][-b backup-boot-sector]\n\ + [-m boot-msg-file][-n volume-name][-i volume-id]\n\ + [-s sectors-per-cluster][-S logical-sector-size][-f number-of-FATs]\n\ + [-h hidden-sectors][-F fat-size][-r root-dir-entries][-R reserved-sectors]\n\ + /dev/name [blocks]\n"); +} + +/* + * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant + * of MS-DOS filesystem by default. + */ +static void check_atari(void) +{ +#ifdef __mc68000__ + FILE *f; + char line[128], *p; + + if (!(f = fopen("/proc/hardware", "r"))) { + perror("/proc/hardware"); + return; + } + + while (fgets(line, sizeof(line), f)) { + if (strncmp(line, "Model:", 6) == 0) { + p = line + 6; + p += strspn(p, " \t"); + if (strncmp(p, "Atari ", 6) == 0) + atari_format = 1; + break; + } + } + fclose(f); +#endif +} + +/* The "main" entry point into the utility - we pick up the options and attempt to process them in some sort of sensible + way. In the event that some/all of the options are invalid we need to tell the user so that something can be done! */ + +int main(int argc, char **argv) +{ + int c; + char *tmp; + char *listfile = NULL; + FILE *msgfile; + struct stat statbuf; + int i = 0, pos, ch; + int create = 0; + unsigned long long cblocks = 0; + int min_sector_size; + + if (argc && *argv) { /* What's the program name? */ + char *p; + program_name = *argv; + if ((p = strrchr(program_name, '/'))) + program_name = p + 1; + } + + gettimeofday(&create_timeval, NULL); + create_time = create_timeval.tv_sec; + volume_id = (u_int32_t) ((create_timeval.tv_sec << 20) | create_timeval.tv_usec); /* Default volume ID = creation time, fudged for more uniqueness */ + check_atari(); + + printf("%s " VERSION " (" VERSION_DATE ")\n", program_name); + + while ((c = getopt(argc, argv, "aAb:cCf:F:Ii:l:m:n:r:R:s:S:h:v")) != EOF) + /* Scan the command line for options */ + switch (c) { + case 'A': /* toggle Atari format */ + atari_format = !atari_format; + break; + + case 'a': /* a : skip alignment */ + align_structures = FALSE; + break; + + case 'b': /* b : location of backup boot sector */ + backup_boot = (int)strtol(optarg, &tmp, 0); + if (*tmp || backup_boot < 2 || backup_boot > 0xffff) { + printf("Bad location for backup boot sector : %s\n", optarg); + usage(); + } + break; + + case 'c': /* c : Check FS as we build it */ + check = TRUE; + malloc_entire_fat = TRUE; /* Need to be able to mark clusters bad */ + break; + + case 'C': /* C : Create a new file */ + create = TRUE; + break; + + case 'f': /* f : Choose number of FATs */ + nr_fats = (int)strtol(optarg, &tmp, 0); + if (*tmp || nr_fats < 1 || nr_fats > 4) { + printf("Bad number of FATs : %s\n", optarg); + usage(); + } + break; + + case 'F': /* F : Choose FAT size */ + size_fat = (int)strtol(optarg, &tmp, 0); + if (*tmp || (size_fat != 12 && size_fat != 16 && size_fat != 32)) { + printf("Bad FAT type : %s\n", optarg); + usage(); + } + size_fat_by_user = 1; + break; + + case 'h': /* h : number of hidden sectors */ + hidden_sectors = (int)strtol(optarg, &tmp, 0); + if (*tmp || hidden_sectors < 0) { + printf("Bad number of hidden sectors : %s\n", optarg); + usage(); + } + break; + + case 'I': + ignore_full_disk = 1; + break; + + case 'i': /* i : specify volume ID */ + volume_id = strtoul(optarg, &tmp, 16); + if (*tmp) { + printf("Volume ID must be a hexadecimal number\n"); + usage(); + } + break; + + case 'l': /* l : Bad block filename */ + listfile = optarg; + malloc_entire_fat = TRUE; /* Need to be able to mark clusters bad */ + break; + + case 'm': /* m : Set boot message */ + if (strcmp(optarg, "-")) { + msgfile = fopen(optarg, "r"); + if (!msgfile) + perror(optarg); + } else + msgfile = stdin; + + if (msgfile) { + /* The boot code ends at offset 448 and needs a null terminator */ + i = MESSAGE_OFFSET; + pos = 0; /* We are at beginning of line */ + do { + ch = getc(msgfile); + switch (ch) { + case '\r': /* Ignore CRs */ + case '\0': /* and nulls */ + break; + + case '\n': /* LF -> CR+LF if necessary */ + if (pos) { /* If not at beginning of line */ + dummy_boot_code[i++] = '\r'; + pos = 0; + } + dummy_boot_code[i++] = '\n'; + break; + + case '\t': /* Expand tabs */ + do { + dummy_boot_code[i++] = ' '; + pos++; + } + while (pos % 8 && i < BOOTCODE_SIZE - 1); + break; + + case EOF: + dummy_boot_code[i++] = '\0'; /* Null terminator */ + break; + + default: + dummy_boot_code[i++] = ch; /* Store character */ + pos++; /* Advance position */ + break; + } + } + while (ch != EOF && i < BOOTCODE_SIZE - 1); + + /* Fill up with zeros */ + while (i < BOOTCODE_SIZE - 1) + dummy_boot_code[i++] = '\0'; + dummy_boot_code[BOOTCODE_SIZE - 1] = '\0'; /* Just in case */ + + if (ch != EOF) + printf("Warning: message too long; truncated\n"); + + if (msgfile != stdin) + fclose(msgfile); + } + break; + + case 'n': /* n : Volume name */ + sprintf(volume_name, "%-11.11s", optarg); + for (i = 0; i < 11; i++) + volume_name[i] = toupper(volume_name[i]); + + break; + + case 'r': /* r : Root directory entries */ + root_dir_entries = (int)strtol(optarg, &tmp, 0); + if (*tmp || root_dir_entries < 16 || root_dir_entries > 32768) { + printf("Bad number of root directory entries : %s\n", optarg); + usage(); + } + break; + + case 'R': /* R : number of reserved sectors */ + reserved_sectors = (int)strtol(optarg, &tmp, 0); + if (*tmp || reserved_sectors < 1 || reserved_sectors > 0xffff) { + printf("Bad number of reserved sectors : %s\n", optarg); + usage(); + } + break; + + case 's': /* s : Sectors per cluster */ + sectors_per_cluster = (int)strtol(optarg, &tmp, 0); + if (*tmp || (sectors_per_cluster != 1 && sectors_per_cluster != 2 + && sectors_per_cluster != 4 && sectors_per_cluster != 8 + && sectors_per_cluster != 16 + && sectors_per_cluster != 32 + && sectors_per_cluster != 64 + && sectors_per_cluster != 128)) { + printf("Bad number of sectors per cluster : %s\n", optarg); + usage(); + } + break; + + case 'S': /* S : Sector size */ + sector_size = (int)strtol(optarg, &tmp, 0); + if (*tmp || (sector_size != 512 && sector_size != 1024 && + sector_size != 2048 && sector_size != 4096 && + sector_size != 8192 && sector_size != 16384 && + sector_size != 32768)) { + printf("Bad logical sector size : %s\n", optarg); + usage(); + } + sector_size_set = 1; + break; + + case 'v': /* v : Verbose execution */ + ++verbose; + break; + + default: + printf("Unknown option: %c\n", c); + usage(); + } + if (optind < argc) { + device_name = argv[optind]; /* Determine the number of blocks in the FS */ + + if (!device_name) { + printf("No device specified.\n"); + usage(); + } + + if (!create) + cblocks = count_blocks(device_name, &orphaned_sectors); /* Have a look and see! */ + } + if (optind == argc - 2) { /* Either check the user specified number */ + blocks = strtoull(argv[optind + 1], &tmp, 0); + if (!create && blocks != cblocks) { + fprintf(stderr, "Warning: block count mismatch: "); + fprintf(stderr, "found %llu but assuming %llu.\n", cblocks, blocks); + } + } else if (optind == argc - 1) { /* Or use value found */ + if (create) + die("Need intended size with -C."); + blocks = cblocks; + tmp = ""; + } else { + fprintf(stderr, "No device specified!\n"); + usage(); + } + if (*tmp) { + printf("Bad block count : %s\n", argv[optind + 1]); + usage(); + } + + if (check && listfile) /* Auto and specified bad block handling are mutually */ + die("-c and -l are incompatible"); /* exclusive of each other! */ + + if (!create) { + check_mount(device_name); /* Is the device already mounted? */ + dev = open(device_name, O_EXCL | O_RDWR); /* Is it a suitable device to build the FS on? */ + if (dev < 0) { + fprintf(stderr, "%s: unable to open %s: %s\n", program_name, + device_name, strerror(errno)); + exit(1); /* The error exit code is 1! */ + } + } else { + loff_t offset = blocks * BLOCK_SIZE - 1; + char null = 0; + /* create the file */ + dev = open(device_name, O_EXCL | O_RDWR | O_CREAT | O_TRUNC, 0666); + if (dev < 0) + die("unable to create %s"); + /* seek to the intended end-1, and write one byte. this creates a + * sparse-as-possible file of appropriate size. */ + if (llseek(dev, offset, SEEK_SET) != offset) + die("seek failed"); + if (write(dev, &null, 1) < 0) + die("write failed"); + if (llseek(dev, 0, SEEK_SET) != 0) + die("seek failed"); + } + + if (fstat(dev, &statbuf) < 0) + die("unable to stat %s"); + if (!S_ISBLK(statbuf.st_mode)) { + statbuf.st_rdev = 0; + check = 0; + } else + /* + * Ignore any 'full' fixed disk devices, if -I is not given. + * On a MO-disk one doesn't need partitions. The filesytem can go + * directly to the whole disk. Under other OSes this is known as + * the 'superfloppy' format. As I don't know how to find out if + * this is a MO disk I introduce a -I (ignore) switch. -Joey + */ + if (!ignore_full_disk && ((statbuf.st_rdev & 0xffffff3f) == 0x0300 || /* hda, hdb */ + (statbuf.st_rdev & 0xffffff0f) == 0x0800 || /* sd */ + (statbuf.st_rdev & 0xffffff3f) == 0x0d00 || /* xd */ + (statbuf.st_rdev & 0xffffff3f) == 0x1600) /* hdc, hdd */ + ) + die("Device partition expected, not making filesystem on entire device '%s' (use -I to override)"); + + if (sector_size_set) { + if (ioctl(dev, BLKSSZGET, &min_sector_size) >= 0) + if (sector_size < min_sector_size) { + sector_size = min_sector_size; + fprintf(stderr, + "Warning: sector size was set to %d (minimal for this device)\n", + sector_size); + } + } else { + if (ioctl(dev, BLKSSZGET, &min_sector_size) >= 0) { + sector_size = min_sector_size; + sector_size_set = 1; + } + } + + if (sector_size > 4096) + fprintf(stderr, + "Warning: sector size is set to %d > 4096, such filesystem will not propably mount\n", + sector_size); + + establish_params(statbuf.st_rdev, statbuf.st_size); + /* Establish the media parameters */ + + setup_tables(); /* Establish the file system tables */ + + if (check) /* Determine any bad block locations and mark them */ + check_blocks(); + else if (listfile) + get_list_blocks(listfile); + + write_tables(); /* Write the file system tables away! */ + + exit(0); /* Terminate with no errors! */ +} diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..a7c41a0 --- /dev/null +++ b/src/version.h @@ -0,0 +1,29 @@ +/* version.h + + Copyright (C) 1998-2005 Roman Hodek + Copyright (C) 2008-2013 Daniel Baumann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + On Debian systems, the complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +#ifndef _version_h +#define _version_h + +#define VERSION "3.0.16" +#define VERSION_DATE "01 Mar 2013" + +#endif -- cgit v1.2.3